题意
有N个BUG,M个补丁,每个补丁有一定的使用时间。补丁使用前有一个字符串标识补丁使用的条件,0代表该位无条件,+代表该位必须有BUG才能使用该补丁,-代表该位必须没有BUG才能使用该补丁。补丁使用完也有一个字符串标识补丁使用的结果,0代表BUG状态没有改变,+代表新增加了BUG,-代表修复了BUG。初始条件为所有位均有BUG,求修复这些BUG所需要的最短时间。若不能修复所有BUG,则输出”Bugs cannot be fixed.”
题解
状态压缩+最短路。将字符串压缩进两个整数里。一个整数负责记录-的位置,另一个整数负责记录+的位置。读取字符串,遇到+时,将记录+字符串的相应位置为1,遇到0时,将记录-字符串的相应位置为1。判断一个整数代表的状态是否符合转移状态,只需要状态(be[1][i]|u)==u&&(be[0][i]&u)==u)即可。
关于上述式子,是这样子得来的。be[1][i]代表记录+位置的整数,+位置标记为1,如果be[1][i]记录的+位置不属于u记录的+位置,则be[1][i]|u!=u。同理,如果be[0][i]记录的-位置不属于u记录的减位置,则be[0][i]&u!=u。
通过be[1][i]|u和be[0][i]&u状态转移,若状态转移后消费时间小于原消费时间,则进行状态转移,并记录到队列中。(Bellman-Ford算法)
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define INF 1e9
using namespace std;
int be[2][110],af[2][110];
int d[1200000];
bool vis[1200000];
int tm[110];
int n,m;
void dijkstra(){
int MAX=(1<<n)-1;
for(int i=0;i<MAX;i++){
vis[i]=false;
d[i]=INF;
}
d[MAX]=0;
queue<int> que;
que.push(MAX);
while(!que.empty()){
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<m;i++){
if((be[1][i]|u)==u&&(be[0][i]&u)==u){
int v=u;
v|=af[1][i];
v&=af[0][i];
if(d[u]+tm[i]<d[v]){
d[v]=d[u]+tm[i];
if(!vis[v]){
que.push(v);
vis[v]=true;
}
}
}
}
}
if(d[0]==INF){
printf("Bugs cannot be fixed.\n");
}else{
printf("Fastest sequence takes %d seconds.\n",d[0]);
}
}
int main()
{
int kase=0;
while(scanf("%d%d",&n,&m)){
if(n+m==0)
break;
memset(be,0,sizeof(be));
memset(af,0,sizeof(af));
for(int i=0;i<m;i++){
char str1[30];
char str2[30];
scanf("%d",&tm[i]);
getchar();
scanf("%s%s",str1,str2);
for(int j=0;j<n;j++){
if(str1[j]=='+'){
be[1][i]+=(1<<j);
}
if(str1[j]!='-'){
be[0][i]+=(1<<j);
}
if(str2[j]=='+'){
af[1][i]+=(1<<j);
}
if(str2[j]!='-'){
af[0][i]+=(1<<j);
}
}
}
printf("Product %d\n",(++kase));
dijkstra();
printf("\n");
}
return 0;
}