- 题意:补丁在修正BUG时,有时也会引入新的BUG,假定有n(n<=20)n(n<=20) 个潜在BUG,和m(m<=100)m(m<=100) 个补丁,每个补丁用两个长度为nn 的字符串表示,其中字符串的每个位置表示一个bug,第一个串表示打补丁之前的状态(“-”表示该BUG必须不存在,“+”表示该补丁必须存在,0表示无所谓),第二串表示打补丁之后的状态(”-“表示不存在,“+”表示存在,0表示不变)。每个补丁有一定的执行时间,你的任务是用最小的时间把所有BUG都存在的软件变得没有BUG。
- 考虑建图与状态压缩,每个状态用一个二进制数表示,0表示‘+’,1表示‘-’,如样例中的-++可表示为(2)100即(10)7;这样每一个状态都可以看成图中的一个点,点与点之间路径的权值则为对应补丁的花费;原题装换成了一个起点为0,终点为2的n次方减1的单源最短路问题。
- *如何松弛?*结点很多,多达2的n次方个,但是补丁的总数只有100个,因此在尝试去松弛时可以遍历这100个补丁,这样相当于每个在**集合(队列)**中的点只有100条出边。
- *如何判断一个状态转移是否合法?*一下为几个位运算技巧:
(A>>n-1)&1用于判断二进制数A从右到左第n位是否为1
A=A|(1<<n-1)用于把A从右往左第n位设为1
A=A&(~(1<<n-i-1))用于把A从右往左第n为设为0
注意,这里的第n位指的是下标从零开始的第n位(0··1··2···n-1); - 代码:状态计算的部分其实可以继续用位运算优化。
#include <iostream>
#include <fstream>
#include <queue>
using namespace std;
const int maxn=(1<<20)+5;
int n,m,d[maxn],num;
struct node{
int u,w;
string s1,s2;
}no[120];
struct heapnode{
int u,d;
bool operator<(const heapnode & a)const{
return d>a.d;}
};
bool dijkstra()
{
for(int i=0;i<=(1<<n)-1;i++)
d[i]=0x3f3f3f3f;
d[0]=0;
priority_queue<heapnode>q;
q.push((heapnode){0,0});
while(!q.empty())
{
heapnode x=q.top();
q.pop();
if(x.d!=d[x.u]) continue;
int u=x.u;
for(int i=1;i<=m;i++)
{
int tmp=0;bool bug=0;
for(int j=0;j<n;j++)
{
if(no[i].s1[j]!='0')
{
bool k=no[i].s1[j]=='+'?0:1;
if(((u>>(n-j-1))&1)!=k)
{
bug=1;
break;
}
}
}
if(!bug)
{
for(int j=0;j<n;j++)
{
if(no[i].s2[j]!='0')
{
bool k=no[i].s2[j]=='+'?0:1;
tmp+=k*(1<<(n-j-1));
}
else{
bool k=((u>>(n-j-1))&1);
tmp+=k*(1<<(n-j-1));
}
}
if(d[tmp]>d[u]+no[i].w)
{
d[tmp]=d[u]+no[i].w;
q.push((heapnode){tmp,d[tmp]});
}
}
}
}
if(d[(1<<n)-1]!=0x3f3f3f3f)
return 1;
return 0;
}
int main()
{
while(cin>>n>>m&&n)
{
for(int i=1;i<=m;i++)
cin>>no[i].w>>no[i].s1>>no[i].s2;
cout<<"Product "<<++num<<endl;
if(dijkstra())
cout<<"Fastest sequence takes "<<d[(1<<n)-1]<<" seconds."<<endl;
else
cout<<"Bugs cannot be fixed."<<endl;
cout<<endl;
}
return 0;
}