软件补丁问题 网络流二十四题

题目描述

T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序。每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。

换句话说,对于每一个补丁 i,都有 2 个与之相应的错误集合 B1[i]和 B2[i],使得仅当软件包含 B1[i]中的所有错误,而不包含 B2[i]中的任何错误时,才可以使用补丁 i。补丁 i 将修复软件中的某些错误 F1[i],而同时加入另一些错误 F2[i]。另外,每个补丁都耗费一定的时间。

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

输入输出格式

输入格式:

第 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 而改变。

输出格式:

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

思路:说是网络流,但是大家好像都是用位压缩+最短路,强行用费用流的话也没什么意义,错误最多二十处,可以用二十位二进制代表每一种状态。根据题意,仅当软件包含 B1[i]中的所有错误,而不包含 B2[i]中的任何错误时,才可以使用补丁 i。补丁 i 将修复软件中的某些错误 F1[i],而同时加入另一些错误 F2[i]。把每个补丁的b1,b2,f1,f2处理出来,在松弛造作时按照题意进行转移,答案就是全1状态跑到全0状态的最短路。

点击打开链接

#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<21;
const int INF=1e9;
struct Patch{
    int b1,b2,f2,f1,t;
    Patch() {
        b1=b2=f1=f2=t=0;
    }
}p[120];
int n,m,dis[maxn];
bool inq[maxn];
int spfa() {
    memset(inq,0,sizeof(inq));
    for(int i=0;i<=(1<<n);i++) dis[i]=INF;
    int u=(1<<n)-1;
    dis[u]=0;
    queue<int> Q;
    inq[u]=true;
    Q.push(u);
    while(!Q.empty()) {
        u=Q.front();
        Q.pop();
        inq[u]=false;
        for(int i=1;i<=m;i++) {
            if((p[i].b1 & u)==p[i].b1  && (p[i].b2&u)==0 ) {
                int v=u&(~p[i].f1)|p[i].f2;
                if(v==u) continue;
                if(dis[v] > dis[u]+p[i].t) {
                    dis[v]=dis[u]+p[i].t;
                    if(!inq[v]) {
                        Q.push(v);
                        inq[v]=true;
                    }
                }
            }
        }
    }
    return dis[0];
}
int main() {
    while(scanf("%d%d",&n,&m) != EOF) {
        string s;
        for(int i=1;i<=m;i++) {
            int k=1;
            scanf("%d",&p[i].t);
            cin>>s;
            int l=s.length();
            for(int j=0;j<l;j++) {
                if(s[j]=='+') {
                    p[i].b1^=k;
                }
                else if(s[j]=='-') {
                    p[i].b2^=k;
                }
                k=k<<1;
            }
            cin>>s;
            k=1;
            l=s.length();
            for(int j=0;j<l;j++) {
                if(s[j]=='+') {
                    p[i].f2^=k;
                }
                else if(s[j]=='-') {
                    p[i].f1^=k;
                }
                k=k<<1;
            }
        }
        int ans=spfa();
        if(ans==INF) {
            puts("0");
        }
        else {
            printf("%d\n",ans);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值