P2761 软件补丁问题 题解

软件补丁问题

题目描述

T 公司发现其研制的一个软件中有 n n n 个错误,随即为该软件发放了 m m m 个补丁程序。

每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。

换句话说,对于任意一个补丁 i i i,都有四个与之相应的集合 B 1 i , B 2 i , F 1 i B1_i,B2_i,F1_i B1i,B2i,F1i F 2 i F2_i F2i。仅当软件包含 B 1 i B1_i B1i 中的所有错误,而不包含 B 2 i B2_i B2i 中的任何错误时,才可以使用补丁 i i i。补丁 i i i 将修复软件中的某些错误集合 F 1 i F1_i F1i,而同时加入另一些错误 F 2 i F2_i F2i。另外,运行每个补丁都耗费一定的时间。

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

输入格式

第一行有两个正整数 n n n m m m n n n 表示错误总数, m m m表示补丁总数。

接下来 m m m 行给出了 m m m 个补丁的信息。每行包括一个正整数,表示运行补丁程序 i i i 所需时间,以及两个长度为 n n n 的字符串。中间用一个空格符隔开。

第一个字符串中,如果第 k k k 个字符为 +,则表示第 k k k 个错误属于 B 1 i B1_i B1i。若为 -,则表示第 k k k 个错误属于 B 2 i B2_i B2i。若为 0,则第 k k k 个错误既不属于 B 1 i B1_i B1i 也不属于 B 2 i B2_i B2i,即软件中是否包含第 k k k 个错误并不影响补丁 i i i 的可用性。

第二个字符串中,如果第 k k k 个字符为 -,则表示第 k k k 个错误属于 F 1 i F1_i F1i。若为 +,则表示第 k k k 个错误属于 F 2 i F2_i F2i。若为 0,则第 k k k 个错误既不属于 F 1 i F1_i F1i 也不属于 F 2 i F2_i F2i,即软件中是否包含第 k k k 个错误不会因使用补丁 i i i 而改变。

输出格式

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

样例 #1

样例输入 #1

3 3
1 000 00-
1 00- 0-+
2 0-- -++

样例输出 #1

8

提示

对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 20 1\le n\le 20 1n20 1 ≤ m ≤ 100 1\le m\le 100 1m100

题解

其实这道题可以把状态(剩余BUG的状压)看作一个点,然后用SPFA就行了,然后枚举边的时候只需要简单的判断一下就行了,没必要连边

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 21, maxm = 210;
int dp[1 << maxn];
int n, m;
int B1[maxm], B2[maxm], F1[maxm], F2[maxm];
int cost[maxm];
bool book[1 << maxn];
char ch[maxn + 10];
queue<int> que;
signed main(){
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i++){
        scanf("%d",&cost[i]);
        scanf("%s",ch);
        for(int j = 0;j < n;j++){
            if(ch[j] == '+')
                B1[i] |= (1 << j);
            else if(ch[j] == '-')
                B2[i] |= (1 << j);
        }
        scanf("%s",ch);
        for(int j = 0;j < n;j++){
            if(ch[j] == '-')
                F1[i] |= (1 << j);
            else if(ch[j] == '+')
                F2[i] |= (1 << j);
        }
    }
    memset(dp,0x3f,sizeof(dp)); dp[(1 << n) - 1] = 0;
    que.push((1 << n) - 1); book[(1 << n) - 1] = true;
    while(!que.empty()){
        int sta = que.front();
        for(int i = 1;i <= m;i++){
            if((sta & B1[i]) == B1[i] && (sta & B2[i]) == 0){
                int nsta = ((sta | F1[i]) | F2[i]) ^ F1[i];
                if(dp[nsta] > dp[sta] + cost[i]){
                    dp[nsta] = dp[sta] + cost[i];
                    if(!book[nsta]){
                        que.push(nsta);
                        book[nsta] = true;
                    }
                }
            }
        }
        que.pop();book[sta] = false;
    }
    if(dp[0] == 0x3f3f3f3f)puts("0");
    else printf("%d\n",dp[0]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值