補丁在修正\(BUG\)時,有時也會引入新的\(BUG\),假定有\(n(n<=20)\)個潛在\(BUG\),和\(m(m<=100)\)個補丁,每一個補丁用兩個長度爲\(n\)的字符串表示,其中字符串的每一個位置表示一個\(BUG\),第一個串表示打補丁以前的狀態 (「-」表示該\(BUG\)必須不存在,「+」表示該補丁必須存在,0表示無所謂),第二串表示打補丁以後的狀態 ("-"表示不存在,"+"表示存在,"0"表示不變)。每一個補丁有必定的執行時間,你的任務是用最小的時間把全部BUG都存在的軟件變得沒有\(BUG\)。ios
\(\text{隱式圖}\)\(SPFA\)c++
在任意時刻,每一個\(BUG\)可能存在也可能不存在,因此能夠用\(n\)位二進制串來表示當前軟件的狀態。打完補丁以後,軟件的BUG狀態會發生改變,對應狀態轉移。是否是很像動態規劃?惋惜動態規劃是行不通的,由於狀態通過屢次轉移以後可能會回到之前的狀態,即狀態圖並非DAG。若是直接用記憶化搜索,會出現無限遞歸。算法
正確的方法是把狀態當作點,狀態轉移當作邊,轉化成圖論中的最短路徑問題,而後使用\(Dijkstra\)或\(Bellman-Ford\)算法進行求解。不過這道題和普通的最短路徑問題不同:節點不少,有\(2^n\)個,並且不少狀態根本遇不到(即無論怎麼打補丁,也不可能打成那種狀態),因此沒有必要先將原圖存儲好。spa
孩子咳嗽老很差, 怎麼辦呢?code
這裏介紹一種 "隱式圖" 的方法,當須要獲得某個點的全部邊時,不是去讀\(G[u]\),而是直接枚舉這\(m\)個補丁是否打的上。無論是\(Dijkstra\)仍是\(Bellman-Ford\)算法,這個方法都適用。遞歸
獲得\(x\)的二進制右起第\(i\)位:
x>>(i-1)&1
ci
把\(x\)二進制的右起第\(i\)位替換爲\(a\)(\(a\)或\(0\)或\(1\)):
x^=(x&(1<<(i-1)))^(a<<(i-1))
字符串
思路也說得很清楚了,這裏就不寫註釋了get
#include<bits/stdc++.h> using namespace std; const int MAXN=20+10,MAXM=100+10; int n,m; struct Node { int t; int a[MAXN]; int b[MAXN]; }patch[MAXM]; int d[2000000]; int T; inline void init(int k) { cin>>patch[k].t; string s; cin>>s; for(int i=0;i<s.size();i++) { if(s[i]=='-')patch[k].a[i+1]=-1; else if(s[i]=='0')patch[k].a[i+1]=0; else patch[k].a[i+1]=1; } cin>>s; for(int i=0;i<s.size();i++) { if(s[i]=='-')patch[k].b[i+1]=-1; else if(s[i]=='0')patch[k].b[i+1]=0; else patch[k].b[i+1]=1; } } inline bool check(int sum,int k) { for(int i=1;i<=n;i++) { if(patch[k].a[i]==0)continue; if(patch[k].a[i]==-1 && (sum>>(n-i)&1)==0 )continue; if(patch[k].a[i]==1 && (sum>>(n-i)&1)==1 )continue; return 0; } return 1; } inline int get(int sum,int k) { for(int i=1;i<=n;i++) { if(patch[k].b[i]==0)continue; if(patch[k].b[i]==-1)sum^=(sum&(1<<(n-i)))^(0<<(n-i)); else sum^=(sum&(1<<(n-i)))^(1<<(n-i)); } return sum; } inline void SPFA() { memset(d,0x3f,sizeof(d)); queue<int>q; q.push((1<<n)-1); d[(1<<n)-1]=0; while(q.size()) { int now=q.front(); //cout<<now<<endl; q.pop(); for(int i=1;i<=m;i++) { if(!check(now,i))continue; int x=get(now,i); //cout<<x<<endl; if(d[now]+patch[i].t<d[x]) { d[x]=d[now]+patch[i].t; q.push(x); } } } } int main() { //ios::sync_with_stdio(false); while(cin>>n>>m) { if(n==0&&m==0)break; T++; for(int i=1;i<=m;i++) init(i); //cout<<patch[1].a[1]<<" "<<patch[1].a[2]<<" "<<patch[1].a[3]<<endl; SPFA(); printf("Product %d\n",T); if(d[0]==0x3f3f3f3f) printf("Bugs cannot be fixed.\n"); else printf("Fastest sequence takes %d seconds.\n",d[0]); cout<<endl; } return 0; }
劉汝佳大法好!string