UVA 658 It‘s not a Bug,it‘s a Feature(单源最短路,Dijkstra)

UVA 658 It’s not a Bug,it’s a Feature(单源最短路,Dijkstra)

有n(n<=20)个潜在bug和m(m<=100)个补丁,每个补丁用两个长度为n的字符串表示,分别代表该补丁的前置环境和安装补丁后的状态。

在第一个表示前置环境的字符串中,0代表该位对应的Bug可有可无,+表示必须有该Bug,-表示必须没有该Bug;在第二个表示安装补丁后的状态的字符串内,0代表该Bug不变,+表示增加了该位Bug,-表示修复了该位Bug。

之前在动态规划中有过用二进制表示集合的经验(状态压缩),对于有n(n<=20)个状态位,每个状态只有0和1两个状态,显然可以用一个二进制的整数来表示一个单独的状态。

这样来,补丁的前置环境可化为一个整数节点u,安装补丁之后的状态可化为一个整数节点v,那么一个补丁就可以形象的表示为多个E(u,v)的有向边。

问题转换成在一张有向有环无负权的图,求单源最短路径的问题。该问题典型的一个算法是Dijkstra。

如果在表示状态中用1表示该位Bug存在,0表示不存在,那么该源的标号为s=(1<

整张图的节点有2n个,然而许多的节点是根本无法到达的,如果直接建图的话会浪费时间和内存(因为包含了许多无用节点和无用边)。

可以采用的一个方法是,在Dijkstra,当从优先队列取出一个节点时,遍历补丁通过位运算的方法看有无可以延伸的边,如果有则延伸并将新节点加入队列。其余的步骤跟Dijkstra算法无差别。

代码如下:

#include
using namespace std;
const int max_n=(1<<21);
const int INF=0x3f3f3f3f;
int n,m;
struct HeapNode{
	int d,u;
	HeapNode(int i,int j):d(i),u(j){}
	bool operator <(const HeapNode& p) const{
		return d>p.d;
	}
};
int d[max_n];
int bug[1005],no_bug[1005],w[1005];
int bug_1[1005],no_bug_1[1005]; 
bool check(int u,int i)
{
	return ((u&no_bug[i])==0)&&((u&bug[i])==bug[i]);
}
int e(int u,int i)
{
	return ((u|no_bug_1[i])^no_bug_1[i])|bug_1[i];
}
void Dijkstra(void)
{
	memset(d,0x3f,sizeof(d));
	int s=(1<<n)-1;
	d[s]=0;
	priority_queue<HeapNode> q;
	q.push(HeapNode(0,s));
	while(!q.empty())
	{
		HeapNode cur=q.top();q.pop();
		int u=cur.u;
		if(d[u]!=cur.d)continue;
		for(int i=0;i<m;i++)
		if(check(u,i))
		{
			int v=e(u,i);
			if(d[v]>d[u]+w[i]){
				d[v]=d[u]+w[i];
				q.push(HeapNode(d[v],v));
			}
		}
	}
}
int main(void)
{
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int cnt=1;
	while(~scanf("%d%d",&n,&m)&&n)
	{
		for(int i=0;i<m;i++)
		{
			string a,b;
			int c;
			cin>>c>>a>>b;
			int x=0,y=0;
			w[i]=c;
			for(int j=0;j<a.length();j++)
			{
				x=(x<<1)+(a[j]=='+'?1:0);
				y=(y<<1)+(a[j]=='-'?1:0);
			}
			bug[i]=x;no_bug[i]=y;
			x=y=0;
			for(int j=0;j<b.length();j++)
			{
				x=(x<<1)+(b[j]=='+'?1:0);
				y=(y<<1)+(b[j]=='-'?1:0);
			}
			bug_1[i]=x;no_bug_1[i]=y;
		}
		Dijkstra();
		printf("Product %d\n",cnt++);
		if(d[0]!=INF)printf("Fastest sequence takes %d seconds.\n",d[0]);
		else printf("Bugs cannot be fixed.\n");
		cout<<endl;
	}
//	fclose(stdin);
//	fclose(stdout);
}
//3 3
//1 000 00-
//1 00- 0-+
//2 0-- -++
//4 1
//7 0-0+ ----
//0 0

上述程序通过bug[]和no_bug[]数组来维护每个补丁前置环境的状态,通过bug_1[]和no_bug_1[]来维护用了该补丁后的状态。

你可能感兴趣的:(图论,ACM,acm竞赛,图论)