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<<n)-1,答案为d[0]。
整张图的节点有2n个,然而许多的节点是根本无法到达的,如果直接建图的话会浪费时间和内存(因为包含了许多无用节点和无用边)。
可以采用的一个方法是,在Dijkstra,当从优先队列取出一个节点时,遍历补丁通过位运算的方法看有无可以延伸的边,如果有则延伸并将新节点加入队列。其余的步骤跟Dijkstra算法无差别。
代码如下:
#include<bits/stdc++.h>
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[]来维护用了该补丁后的状态。