题目:洛谷P2761、vijos P1019、codevs1239、codevs2218。
题目大意:有n个错误,m个不同的补丁。
对于一个补丁,有两个不同的字符串描述。具体如下:
如果当前错误包含第一个字符串中“+”的所有错误,且不包含第一个字符串中“-”的所有错误,则可以用这个补丁。
使用这个补丁将修复第二个字符串中“-”的所有错误,但会增加第二个字符串中“+”的错误。
使用每个补丁都要耗费一定的时间。
现在这些错误都有,每个补丁可以重复安装,问你最短要多长时间能修复所有错误,如果无法完成,输出0。
解题思路:并没有看出来要用什么网络流。
首先最多20个错误,用32位整数的每一位保存一种错误,完全没有问题。
然后,可以发现本题是一个最短路问题,要从最初有全部错误的状态转移到最终无错误的状态,其中有很多状态,且有很多连边。
但是状态数太多,没法保存边怎么办?
直接在SPFA/Dijkstra里枚举要用的补丁即可。
在这之中的一些判断就要用到位运算,不懂的可以百度。
到0的最短路长度即为答案。
如果发现无答案,则输出0。
C++ Code:
#include<cstdio> #include<queue> #include<cstring> int n,m,dis[1<<21]; bool vis[1<<21]; struct buding{ int has,donthas,ac,wa,t;//has表示要有这些错误,donthas表示不能有这些错误,ac表示可以修复这些错误,wa表示会添加这些错误,t表示时间。 buding():has(0),donthas(0),ac(0),wa(0){} }e[125]; char b[30],f[30]; std::queue<int>q; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ scanf("%d%s%s",&e[i].t,b,f); for(int j=0;j<n;++j){ if(b[j]=='+')e[i].has|=1<<j;else if(b[j]=='-')e[i].donthas|=1<<j; if(f[j]=='+')e[i].wa|=1<<j;else if(f[j]=='-')e[i].ac|=1<<j; } } q.push((1<<n)-1); memset(dis,0x3f,sizeof dis); dis[(1<<n)-1]=0; memset(vis,0,sizeof vis); vis[(1<<n)-1]=1; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=1;i<=m;++i) if(((u&e[i].has)==e[i].has)&&(!(u&e[i].donthas))){ int to=(~((~u)|e[i].ac))|e[i].wa; if(dis[to]>dis[u]+e[i].t){ dis[to]=dis[u]+e[i].t; if(!vis[to]){ vis[to]=true; q.push(to); } } } } if(dis[0]==0x3f3f3f3f)puts("0");else printf("%d ",dis[0]); return 0; }