【状压】软件补丁问题

软件补丁问题

不知道为什么放在网络流24题里面,我感觉是状压。

题面

一个软件中有(n)个错误,共(m)个补丁程序。每一个补丁程序都有其特定的适用环境。对于每一个补丁(i)都有(2)个与之相应的错误集合(B1[i])和,(B2[i])使得仅当软件包含(B1[i])中的所有错误,而不包含(B2[i])中的任何错误时,才可以使用补丁(i)。补丁(i)将修复软件中的某些错误(F1[i]),而同时加入另一些错误(F2[i])。每个补丁都耗费一定的时间。

试设计一个算法,利用(m)个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的(n)个错误和(m)个补丁程序,找到总耗时最少的软件修复方案。

输入格式

第 1 行有 2 个正整数(n)(m)(n)表示错误总数,(m)表示补丁总数,(1leq nleq20), (1leq mleq 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)而改变。

输出格式

程序运行结束时,将总耗时数输出。如果问题无解,则输出(0)

(Solution)

因为数据范围很小,所以我一开始想到的是状压。

状压的状态就是软件错误的状态。总共是(2^{20})种,开一个数组(dis)表示到达某种状态的最短时间,那么最后的答案就是(dis[0])(我以"(1)"为“有错误”)

首先,读入。

(B1)(B2)分开存。由题面可知,如果(B1|x==x),这说明(x)中包含(B1),若(B2)&(x==0),则说明(B2)(x)没有交集。这样就可以判断某个修补程序是否可用。

(F)的读入方法类似。两个分开存。

int x = now ^ (now & f1[i]);
x |= f2[i];

(now)是当前状态,(x)是使用修补程序后的状态。这个式子应该不难推。

剩下的就是跑最短路了。相当于在状态之间连边,“距离”为最小时间。

(code)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
long long read(){
    long long x = 0; int f = 0; char c = getchar();
    while(c < '0' || c > '9') f |= c == '-', c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f? -x:x;
}

int n, m, S, t[105], b1[105], b2[105], f1[105], f2[105], dis[1<<21];
bool vis[1<<21];

bool judge(int x, int u){//判断程序是否可用
    if((b1[u] | x) != x) return false;
    if(b2[u] & x) return false;
    return true;
}

void spfa(){
    memset(dis, 0x3f3f3f3f, sizeof dis);
    queue<int> q;
    q.push(S), dis[S]=0;
    while(!q.empty()){
        int now = q.front(); q.pop();
        for(int i = 1; i <= m; ++i){
            if(!judge(now, i)) continue;
            int x = now ^ (now & f1[i]);
            x |= f2[i];//计算新的状态
            if(dis[x] > dis[now] + t[i]){
                dis[x] = dis[now] + t[i];
                if(!vis[x]) q.push(x), vis[x] = 1;
            }
        }
        vis[now] = 0;
    }
}

string s;
int main(){
    n = read(), m = read();
    S = 1 << n, --S;
    for(int i = 1; i <= m; ++i){
        t[i] = read(), cin >> s;//读入
        for(int j = 0; j < n; ++j)
            if(s[j] == '+') b1[i] += 1 << j;
            else if(s[j] == '-') b2[i] += 1 << j;
        cin >> s;
        for(int j = 0; j < n; ++j)
            if(s[j] == '-') f1[i] += 1 << j;
            else if(s[j] == '+') f2[i] += 1 << j;
    }
    spfa();
    if(dis[0] == 0x3f3f3f3f) puts("0");//特判
    else printf("%d", dis[0]);
    return 0;
}
原文地址:https://www.cnblogs.com/kylinbalck/p/10600395.html