洛谷 題解 UVA658 【這不是bug,而是特性 It's not a Bug, it's a Feature!】
【題意】
補丁在修正\(BUG\)時,有時也會引入新的\(BUG\),假定有\(n(n<=20)\)個潛在\(BUG\),和\(m(m<=100)\)個補丁,每個補丁用兩個長度為\(n\)的字串表示,其中字串的每個位置表示一個\(BUG\),第一個串表示打補丁之前的狀態 (“-”表示該\(BUG\)必須不存在,“+”表示該補丁必須存在,0表示無所謂),第二串表示打補丁之後的狀態 ("-"表示不存在,"+"表示存在,"0"表示不變)。每個補丁有一定的執行時間,你的任務是用最小的時間把所有BUG都存在的軟體變得沒有\(BUG\)。
【演算法】
\(\text{隱式圖}\)\(SPFA\)
【分析】
在任意時刻,每個\(BUG\)可能存在也可能不存在,所以可以用\(n\)位二進位制串來表示當前軟體的狀態。打完補丁之後,軟體的BUG狀態會發生改變,對應狀態轉移。是不是很像動態規劃?可惜動態規劃是行不通的,因為狀態經過多次轉移之後可能會回到以前的狀態,即狀態圖並不是DAG。如果直接用記憶化搜尋,會出現無限遞迴。
正確的方法是把狀態看成點,狀態轉移看成邊,轉化成圖論中的最短路徑問題,然後使用\(Dijkstra\)或\(Bellman-Ford\)演算法進行求解。不過這道題和普通的最短路徑問題不一樣:節點很多,有\(2^n\)個,而且很多狀態根本遇不到(即不管怎麼打補丁,也不可能打成那種狀態),所以沒有必要先將原圖儲存好。
孩子咳嗽老不好, 怎麼辦呢?
這裡介紹一種 "隱式圖" 的方法,當需要得到某個點的所有邊時,不是去讀\(G[u]\),而是直接列舉這\(m\)個補丁是否打的上。不管是\(Dijkstra\)還是\(Bellman-Ford\)演算法,這個方法都適用。
- 一些本題的其他小技巧
得到\(x\)的二進位制右起第\(i\)位:
x>>(i-1)&1
把\(x\)二進位制的右起第\(i\)位替換為\(a\)(\(a\)或\(0\)或\(1\)):
x^=(x&(1<<(i-1)))^(a<<(i-1))
【程式碼】
思路也說得很清楚了,這裡就不寫註釋了
#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;
}
劉汝佳大法好