【状压】软件补丁问题

软件补丁问题

不知道为什么放在网络流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\)表示补丁总数,\(1\leq n\leq20\), \(1\leq m\leq 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;
}
posted @ 2019-03-26 15:23  Kylin_Seven  阅读(204)  评论(0编辑  收藏  举报