题目大意:有N个bug,M个补丁,要求用最短的时间将N个bug补上。给补丁的描述为:第一个参数是个整形,表示用这个补丁修补所需要的时间,第二个参数是个字符串,表示使用这个补丁的前提条件,字符串里面的‘0’表示这个位置的bug可存在也可不存在,'+'表示这个位置的bug一定要存在,'-'表示这个位置的bug一定不能存在,所谓的这个位置指的是该字符的下标,字符串每个字符的下标对应一个bug,第三个参数是字符串,表示使用这个补丁后,原来的状态会变成怎么样,‘0’表示这个位置的bug状态不变,'+'表示这个bug一定要存在,'-'表示这个bug一定要不存在,状态量很多,而且有的状态量是不可能存在的,所以一一枚举的话明显是不行的,那就以u表示原始状态,然后在再遍历补丁,看能否使用,如果能的话,就使用,然后改变状态,既然要求是最短的时间的话,而且bug要全部补上,就相当于要求最后的状态0,初始状态就为(1 << N) - 1,每个位上的1对应响应位置的bug,如果是1,表示该位置还有bug,如果是0,表示该位置bug已经补好
现在的难点是如果判断补丁能否使用和使用补丁后的状态变成了什么,既然用二进制表示状态的话,就用二进制表示补丁的状态了,设初始状态为X
1.要判断某些位置为1的话,用 X & 这个补丁的要求 看是否会等于X
2.要判断某些位置为0的话,用X | 这个补丁的要求 看是否会等于X
3.要将某些位置变为1的话,就 X |= 使用这个补丁后的变化
4.要将某些位置变为0的话,就 X &= 使用这个补丁后的变化
好难解释,以上是参考Staginner大神的,如果不懂的话可以去看看他的点击打开链接
这题题意比较复杂,如果有疑问的话,欢迎提问
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define maxn 110
#define maxN (1 << 20) + 10
#define INF 0x3f3f3f3f
int vis[maxN],d[maxN];
int s[2][maxn],t[2][maxn];
char str1[maxn],str2[maxn];
int N,M,w[maxn];
void spfa() {
int MAX = (1 << N) - 1;
queue<int> q;
for(int i = 0; i < MAX; i++) {
vis[i] = 0;
d[i] = INF;
}
d[MAX] = 0;
q.push(MAX);
int u,v;
while(!q.empty()) {
u = q.front();
vis[u] = 0;
q.pop();
for(int i = 0; i < M; i++)
if((u | s[1][i]) == u && (u & s[0][i]) == u) {
v = u;
v |= t[1][i];
v &= t[0][i];
if(d[u] + w[i] < d[v]) {
d[v] = d[u] + w[i];
if(!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
if(d[0] == INF)
printf("Bugs cannot be fixed.\n");
else
printf("Fastest sequence takes %d seconds.\n",d[0]);
}
int main() {
int mark = 1;
while(scanf("%d%d",&N,&M) != EOF && N + M) {
memset(s,0,sizeof(s));
memset(t,0,sizeof(t));
for(int i = 0; i < M; i++) {
scanf("%d%s%s",&w[i],str1,str2);
for(int j = 0; j < N; j++) {
if(str1[j] == '+')
s[1][i] += (1 << j);
if(str1[j] != '-')
s[0][i] += (1 << j);
if(str2[j] == '+')
t[1][i] += (1 << j);
if(str2[j] != '-')
t[0][i] += (1 << j);
}
}
printf("Product %d\n",mark++);
spfa();
printf("\n");
}
return 0;
}