P2761 软件补丁问题
题目背景
none!
题目描述
T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序。每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。
换句话说,对于每一个补丁 i,都有 2 个与之相应的错误集合 B1[i]和 B2[i],使得仅当软件包含 B1[i]中的所有错误,而不包含 B2[i]中的任何错误时,才可以使用补丁 i。补丁 i 将修复软件中的某些错误 F1[i],而同时加入另一些错误 F2[i]。另外,每个补丁都耗费一定的时间。
试设计一个算法,利用 T 公司提供的 m 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的 n 个错误和 m 个补丁程序,找到总耗时最少的软件修复方案。
输入输出格式
输入格式:
第 1 行有 2 个正整数 n 和 m,n 表示错误总数,m表示补丁总数,1<=n<=20, 1<=m<=100。
接下来 m 行给出了 m 个补丁的信息。每行包括一个正整数,表示运行补丁程序 i 所需时间,以及 2 个长度为 n 的字符串,中间用一个空格符隔开。
第 1 个字符串中,如果第 k 个字符 bk 为“+”,则表示第 k 个错误属于 B1[i],若为“-”,则表示第 k 个错误属于 B21[i],若为“0”,则第 k 个错误既不属于 B1[i]也不属于 B2[i],即软件中是否包含第 k 个错误并不影响补丁 i 的可用性。
第 2 个字符串中,如果第 k 个字符 bk为“-”,则表示第 k 个错误属于 F1[i],若为“+”,则表示第 k 个错误属于 F2[i],若为“0”,则第 k 个错误既不属于 F1[i]也不属于 F2[i],即软件中是否包含第 k 个错误不会因使用补丁i 而改变。
输出格式:
程序运行结束时,将总耗时数输出。如果问题无解,则输出 0。
输入输出样例
说明
none!
//。。。。 //竟然是个状压DP //....还是spfa转移 //太菜了 //因为最多只有20个补丁,所以我们可以开一个1<<20的数组来记录每个病毒的状态 //一开始有n个病毒,也就是起始点是(1<<n)-1,目标点就是0,也就是一个病毒都没有 //我们记录每个补丁的b1,b2,f1,f2, //如果这个补丁可以修复,那就b1[]和当前状态每一位|1之后还是n,和b2[] & 之后==0 //然后产生的影响就是f1[]中的位=0,f2[]中的位=1 //。。。修复和添加错误的时候不能用减法。。。 //因为修复的错误可能当前状态里没有,或者添加的错误当前状态已经有了 //可以让f1和now & 一下=tmp,找出哪些病毒是这个补丁可以修复的,然后再让now^=tmp,这样就把修复的病毒变成了0 //添加病毒的话直接让now|=f2就可以了 //....还是不对,40分了,多得了20分。。。 //judge函数里要加括号。。 //优先级真tnd #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=21; const int M=105; int n,m,S; int tim[M]; int b1[M],b2[M],f1[M],f2[M]; int dis[1<<N]; bool vis[1<<N]; bool judge(int sta,int id) { if((b1[id]|sta)!=sta) return false; if(b2[id]&sta) return false; return true; } void spfa() { memset(dis,0x3f,sizeof(dis)); queue<int> que; que.push(S),dis[S]=0; int now,x; while(!que.empty()) { now=que.front(),que.pop(); for(int i=1;i<=m;++i) { if(!judge(now,i)) continue; x=now^(now&f1[i]); x|=f2[i]; if(dis[x]>dis[now]+tim[i]) { dis[x]=dis[now]+tim[i]; if(!vis[x]) { que.push(x); vis[x]=1; } } } vis[now]=0; } } char s[M]; int main() { scanf("%d%d",&n,&m); S=1<<n,--S; for(int i=1;i<=m;++i) { scanf("%d",tim+i); scanf("%s",s); for(int j=0;j<n;++j) { if(s[j]=='+') //1<<j就是第几个错误,+=就是把那位变成1 b1[i]+=1<<j; else if(s[j]=='-') b2[i]+=1<<j; } scanf("%s",s); for(int j=0;j<n;++j) { if(s[j]=='-') f1[i]+=1<<j; else if(s[j]=='+') f2[i]+=1<<j; } } spfa(); if(dis[0]==0x3f3f3f3f) printf("0"); else printf("%d",dis[0]); return 0; }