题意:有N个潜在的bug和m个补丁,每个补丁用长为N的字符串表示。首先输入bug数目以及补丁数目。然后就是对M个补丁的描述,共有M行。每行首先是一个整数,表明打该补丁所需要的时间。然后是两个字符串,第一个字符串是对软件的描述,只有软件处于该状态下才能打该补丁该字符串的每一个位置代表bug状态("-"代表该位置没bug,"+"代表该位置有bug,"0"表示该位置无论有没有bug都可打补丁)。然后第二个字符串是对打上补丁后软件状态的描述"-"代表该位置上的bug已经被修复,"+"表示该位置又引入了一个新的bug, "0"表示该位置跟原来状态一样)。要求用最少时间完成对软件的修复,即将所有位置全都置为0。(N≤20)
解法:对每个bug用二进制表示,补丁两端的点通过边来转换,共2^n-1个结点。由于这样的点太多了,而实际上我们不需要这么多点。于是便像“隐式图搜索”一样,不知道所有的点,便通过所有的边来拓展。在这题中,也就是不是像平常一样用邻接表来拓展,而是枚举所有补丁,看是否能用上这条边。(注意"0"既可表示"+",也可表示"-")这样就可以从源点“11...11”开始用Dijkstra边求解边建图,直到"00...00"。
注意——由于用到了二进制的位运算,该加括号的地方千万不要漏;Dijkstra中优先队列的top()是汇点的值时就可以跳出,因为剩下的可以拓展的点都比它的值大,由于没有负权边,就不可能通过一些边又比它小了。
另外,本来我想偷懒不分析时间复杂度的,但是!!(o´・ェ・`o)我发现Dijkstra+优先队列的算法的时间是spfa的好几倍啊!于是我还是分析了一下......
Dijkstra+优先队列:由于是类“隐式图搜索”,所以每个点进行拓展的操作都是m。原先的时间复杂度是O(n log n+m),其中加的m就是每个点拓展时总共访问过一次的边。而此时这里要+km,k表示在汇点出队前入队的点树,因为每个点拓展时都遍历了一次m条所有的边啊。因此总的是O(n log n+km),最坏情况O(n log n+nm),而最坏的点是2^n。o(─.─|||
spfa:原来的时间复杂度是O(km),k为每个节点进入队列的次数,且k一般<=2。而这里尽管每个点拓展时遍历了所有边,但依旧没有使它的复杂度改变多少!!!∑(゚Д゚ノ)ノ 因为它本来主要承担这个时间复杂度的就是这个"km"啊!
总的来说就是——类“隐式图搜索”还是用spfa吧。
你们可以看看代码感受一下~ ╮(╯_╰)╭ P.S.我打得真的好粗心,所以注释有一点点儿多啊~
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<queue> 6 using namespace std; 7 8 const int N=25,NN=1<<20,M=105,INF=(int)2e9; 9 int n,m; 10 int d[NN],vis[NN]; 11 struct patch{int d;char s[N],ss[N];}a[M]; 12 13 struct node 14 { 15 int bug,d; 16 node() {} 17 node(int u,int v) {bug=u;d=v;} 18 bool operator < (const node& now) const 19 { return d>now.d; }//return!!! 20 }; 21 priority_queue<node> q; 22 23 bool check(int x,int k) 24 { 25 for (int i=0;i<n;i++) 26 { 27 int tmp=1<<i;//反着看二进制也没什么不行的(110表示一般的011),只要没有搞错位数就好了 28 if (a[k].s[i]=='+'&& !(x&tmp)) return false; 29 if (a[k].s[i]=='-'&& (x&tmp)) return false;//() 30 } 31 return true; 32 } 33 int trans(int x,int k) 34 { 35 int full=(1<<n)-1;//() 36 for (int i=0;i<n;i++) 37 { 38 int tmp=1<<i; 39 if (a[k].ss[i]=='+') x|=tmp; 40 if (a[k].ss[i]=='-') x&=(full-tmp);//&(full-tmp) 或 & ~tmp 41 } 42 return x; 43 } 44 int solve() 45 { 46 int tmp=(1<<n)-1;//node:0~tmp 47 while (!q.empty()) q.pop(); 48 for (int i=0;i<=tmp;i++) d[i]=INF,vis[i]=0; 49 node st=node(tmp,0); 50 q.push(st); d[tmp]=0; 51 while (!q.empty()) 52 { 53 int x=q.top().bug; 54 if (!x) return d[0];//notice,别的再更新也是从大于它的d[x]开始更新,无更优的解了 55 q.pop(); 56 if (vis[x]) continue; 57 vis[x]=1; 58 for (int i=1;i<=m;i++) 59 { 60 if (!check(x,i)) continue; 61 int y=trans(x,i); 62 if (d[x]+a[i].d<d[y]) 63 { 64 d[y]=d[x]+a[i].d; 65 q.push(node(y,d[y])); 66 } 67 } 68 } 69 if (d[0]!=INF) return d[0]; 70 return -1; 71 } 72 int main() 73 { 74 int T=0; 75 while (1) 76 { 77 scanf("%d%d",&n,&m); 78 if (!n && !m) break; 79 for (int i=1;i<=m;i++) 80 scanf("%d%s%s",&a[i].d,a[i].s,a[i].ss); 81 int ans=solve(); 82 printf("Product %d\n",++T); 83 if (ans==-1) printf("Bugs cannot be fixed.\n"); 84 else printf("Fastest sequence takes %d seconds.\n",ans); 85 printf("\n"); 86 } 87 return 0; 88 }
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 #include <iostream> 5 #include <cstdlib> 6 #include <algorithm> 7 #include <vector> 8 #include <map> 9 #include <string> 10 #include <set> 11 #include <ctime> 12 #include <cmath> 13 #include <cctype> 14 using namespace std; 15 #define MAX 100000 16 const int maxn = 110; 17 const int INF = 0x3f3f3f3f; 18 #define LL long long 19 int vis[(1<<20)+10]; 20 int d[(1<<20)+10]; 21 int s[2][maxn]; 22 int t[2][maxn]; 23 string s1,s2; 24 int n,m,w[maxn]; 25 void dij() 26 { 27 int Max=(1<<n)-1; 28 queue<int>q; 29 for (int i = 0;i<Max;i++) 30 { 31 vis[i]=0; 32 d[i]=INF; 33 } 34 d[Max]=0; 35 q.push(Max); 36 int u,v; 37 while (!q.empty()) 38 { 39 u=q.front(); 40 vis[u]=0; 41 q.pop(); 42 for (int i = 0;i<m;i++) 43 { 44 if ((u | s[1][i])== u && (u & (s[0][i]))==u) 45 { 46 v=u; 47 v = v | t[1][i]; 48 v = v & t[0][i]; 49 if (d[u]+w[i] <d[v]) 50 { 51 d[v]=d[u]+w[i]; 52 if (!vis[v]) 53 { 54 q.push(v); 55 vis[v]=1; 56 } 57 } 58 } 59 } 60 } 61 if (d[0]==INF) 62 printf("Bugs cannot be fixed.\n"); 63 else 64 printf("Fastest sequence takes %d seconds.\n",d[0]); 65 } 66 int cas=1,T; 67 68 int main() 69 { 70 while (scanf("%d%d",&n,&m)!=EOF && n) 71 { 72 memset(s,0,sizeof(s)); 73 memset(t,0,sizeof(t)); 74 for (int i = 0;i<m;i++) 75 { 76 cin >> w[i] >> s1 >> s2; 77 for (int j = 0;j<n;j++) 78 { 79 if (s1[j]=='+') 80 s[1][i]+=(1<<j); 81 if (s1[j]!='-') 82 s[0][i]+=(1<<j); 83 if (s2[j]=='+') 84 t[1][i]+=(1<<j); 85 if (s2[j]!='-') 86 t[0][i]+=(1<<j); 87 } 88 } 89 printf("Product %d\n",cas++); 90 dij(); 91 printf("\n"); 92 } 93 return 0; 94 }