1. 程式人生 > >[Luogu P2761] 軟件補丁問題

[Luogu P2761] 軟件補丁問題

i++ size OS 每次 表示 for ret long int

Description

T 公司發現其研制的一個軟件中有 n 個錯誤,隨即為該軟件發放了一批共 m 個補丁程序。每一個補丁程序都有其特定的適用環境,某個補丁只有在軟件中包含某些錯誤而同時又不包含另一些錯誤時才可以使用。一個補丁在排除某些錯誤的同時,往往會加入另一些錯誤。

換句話說,對於每一個補丁 i,都有 2 個與之相應的錯誤集合 B1[i]和 B2[i],使得僅當軟件包含 B1[i]中的所有錯誤,而不包含 B2[i]中的任何錯誤時,才可以使用補丁 i。補丁 i 將修復軟件中的某些錯誤 F1[i],而同時加入另一些錯誤 F2[i]。另外,每個補丁都耗費一定的時間。

試設計一個算法,利用 T 公司提供的 m 個補丁程序將原軟件修復成一個沒有錯誤的軟件,並使修復後的軟件耗時最少。對於給定的 n 個錯誤和 m 個補丁程序,找到總耗時最少的軟件修復方案。

Input&Range

第 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

程序運行結束時,將總耗時數輸出。如果問題無解,則輸出 0。

Solution

我們用一個n位二進制數表示當前狀態,從左往右數第i位表示第i個軟件是否有病毒。可以在每次輸入的時候連邊跑最短路,但是會超時。所以要把每個二進制數記錄下來,spfa的時候把建圖和求最短路一起做了=.=

但是有一個地方不明白,當求當前狀態向某一狀態轉移時,為什麽不能這樣:

int j=i^f1|f2;

正解卻是這樣:

int j=i&(~f1)|f2;

第二種理解了,就是f1取反之後再與i表示該在的病毒還是在。那我們再回頭看第一種,第一種可能是因為輸入時有數據會令某個病毒在f1裏出現卻沒有在i中出現,這就導致0^1的出現,把本該是0的地方變成了1,所以wa。

Code

#include<cstdio>
#include<cstring>
#define int long long
using namespace std;

int hd,tail=1;
int q[1100005];
int dis[1100005];
bool in[1100005];
char a[25],b[25];
int ruanjian[105][8];//1--b1 2--b2 3--f1 4--f2 5--t
int n,m,cnt,maxn;

void spfa(int s){
    dis[maxn-1]=0;
    q[1]=s;
    while(hd++<tail){
        int u=q[hd];
        in[u]=0;
        for(int i=1;i<=m;i++){
            int b1=ruanjian[i][1];
            int b2=ruanjian[i][2];
            int f1=ruanjian[i][3];
            int f2=ruanjian[i][4];
            int t=ruanjian[i][5];
            if((u&b1)!=b1||(u&b2)) continue;
            int j=u&(~f1)|f2;
            if(dis[j]>dis[u]+t){
                dis[j]=dis[u]+t;
                if(!in[j]) in[j]=1,q[++tail]=j;
            }
        }
    }
}

signed main(){
    scanf("%lld%lld",&n,&m);maxn=1<<n;
    for(int i=0;i<maxn;i++) dis[i]=1234567890;
    for(int h=1;h<=m;h++){
        scanf("%lld",&ruanjian[h][5]);
        scanf("%s%s",a,b);
        for(int i=0;i<n;i++){
            if(a[i]==+) ruanjian[h][1]|=1<<i;
            if(a[i]==-) ruanjian[h][2]|=1<<i;
            if(b[i]==-) ruanjian[h][3]|=1<<i;
            if(b[i]==+) ruanjian[h][4]|=1<<i;
        }
    }
    spfa(maxn-1);
    printf(dis[0]==1234567890?"0":"%lld",dis[0]);
    return 0;
}

[Luogu P2761] 軟件補丁問題