T 公司發現其研製的一個軟件中有n 個錯誤,隨即爲該軟件發放了一批共m 個補丁程序。每一個補丁程序都有其特定的適用環境,某個補丁只有在軟件中包含某些錯誤而同時又
不包含另一些錯誤時纔可以使用。一個補丁在排除某些錯誤的同時,往往會加入另一些錯誤。換句話說,對於每一個補丁i,都有2 個與之相應的錯誤集合B1[i]和B2[i],使得僅當軟件包含B1[i]中的所有錯誤,而不包含B2[i]中的任何錯誤時,纔可以使用補丁i。補丁i 將修復軟件中的某些錯誤F1[i],而同時加入另一些錯誤F2[i]。另外,每個補丁都耗費一定的時間。試設計一個算法,利用T 公司提供的m個補丁程序將原軟件修復成一個沒有錯誤的軟件,並使修復後的軟件耗時最少。
«編程任務:
對於給定的n個錯誤和m個補丁程序,找到總耗時最少的軟件修復方案。
«數據輸入:
由文件input.txt 提供輸入數據。文件第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 而改變。
«結果輸出:
程序運行結束時,將總耗時數輸出到文件output.txt中。如果問題無解,則輸出0。
輸入文件示例 輸出文件示例
3 3 8
1 000 00-
1 00- 0-+
2 0-- -++
【問題分析】
求一個狀態到另一個狀態變換的最少費用,最短路徑問題。
【建模方法】
軟件的狀態用二進制位表示,第i位爲第i個錯誤是否存在。把每個狀態看做一個頂點,一個狀態應用一個補丁到達另一狀態,連接一條權值爲補丁時間的有向邊。求從初始狀態到目標狀態的最短路徑即可。
#include<cstdio>
using namespace std;
const int mm=111;
const int mn=1<<22;
int i,j,k,n,m,cost[mm],a1[mm],a2[mm],b1[mm],b2[mm],f[mn];
char s1[22],s2[22];
void dfs(int now)
{
if(f[now]>=f[0])return;
for(int i=1,tmp;i<=m;++i)
if((now&a1[i])==a2[i])
{
tmp=(now&b1[i])|b2[i];
if(f[now]+cost[i]<f[tmp])f[tmp]=f[now]+cost[i],dfs(tmp);
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=-1)
{
for(i=0;i<mn;++i)f[i]=mn;
for(i=1;i<=m;++i)
{
scanf("%d%s%s",&cost[i],s1,s2);
a1[i]=a2[i]=b1[i]=b2[i]=0;
for(j=0;j<n;++j)
{
a1[i]<<=1,a2[i]<<=1,b1[i]<<=1,b2[i]<<=1;
if(s1[j]!='0')++a1[i];
if(s1[j]=='+')++a2[i];
if(s2[j]!='-')++b1[i];
if(s2[j]=='+')++b2[i];
}
}
f[(1<<n)-1]=0;
dfs((1<<n)-1);
printf("%d\n",f[0]<mn?f[0]:0);
}
return 0;
}