P2761 软件补丁问题 题解

文章提出了一个算法设计问题,目标是使用补丁程序修复有错误的软件,使得总耗时最少。每个补丁有其特定的适用条件和效果,且修复错误的同时可能引入新错误。输入包括错误和补丁信息,输出要求是最小总耗时。解决方案涉及状态转移和SPFA算法的应用。

软件补丁问题

题目描述

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

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

换句话说,对于任意一个补丁 iii,都有四个与之相应的集合 B1i,B2i,F1iB1_i,B2_i,F1_iB1i,B2i,F1iF2iF2_iF2i。仅当软件包含 B1iB1_iB1i 中的所有错误,而不包含 B2iB2_iB2i 中的任何错误时,才可以使用补丁 iii。补丁 iii 将修复软件中的某些错误集合 F1iF1_iF1i,而同时加入另一些错误 F2iF2_iF2i。另外,运行每个补丁都耗费一定的时间。

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

输入格式

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

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

第一个字符串中,如果第 kkk 个字符为 +,则表示第 kkk 个错误属于 B1iB1_iB1i。若为 -,则表示第 kkk 个错误属于 B2iB2_iB2i。若为 0,则第 kkk 个错误既不属于 B1iB1_iB1i 也不属于 B2iB2_iB2i,即软件中是否包含第 kkk 个错误并不影响补丁 iii 的可用性。

第二个字符串中,如果第 kkk 个字符为 -,则表示第 kkk 个错误属于 F1iF1_iF1i。若为 +,则表示第 kkk 个错误属于 F2iF2_iF2i。若为 0,则第 kkk 个错误既不属于 F1iF1_iF1i 也不属于 F2iF2_iF2i,即软件中是否包含第 kkk 个错误不会因使用补丁 iii 而改变。

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

8

提示

对于 100%100\%100% 的数据:1≤n≤201\le n\le 201n201≤m≤1001\le m\le 1001m100

题解

其实这道题可以把状态(剩余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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值