[Luogu P2761] 软件补丁问题

Description

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

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

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

Input&Range

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

Output

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

Solution

我们用一个n位二进制数表示当前状态,从左往右数第i位表示第i个软件是否有病毒。可以在每次输入的时候连边跑最短路,但是会超时。所以要把每个二进制数记录下来,spfa的时候把建图和求最短路一起做了=.=

但是有一个地方不明白,当求当前状态向某一状态转移时,为什么不能这样:

int j=i^f1|f2;

正解却是这样:

int j=i&(~f1)|f2;

第二种理解了,就是f1取反之后再与i表示该在的病毒还是在。那我们再回头看第一种,第一种可能是因为输入时有数据会令某个病毒在f1里出现却没有在i中出现,这就导致0^1的出现,把本该是0的地方变成了1,所以wa。

Code

#include<cstdio>
#include<cstring>
#define int long long
using namespace std;

int hd,tail=1;
int q[1100005];
int dis[1100005];
bool in[1100005];
char a[25],b[25];
int ruanjian[105][8];//1--b1 2--b2 3--f1 4--f2 5--t
int n,m,cnt,maxn;

void spfa(int s){
    dis[maxn-1]=0;
    q[1]=s;
    while(hd++<tail){
        int u=q[hd];
        in[u]=0;
        for(int i=1;i<=m;i++){
            int b1=ruanjian[i][1];
            int b2=ruanjian[i][2];
            int f1=ruanjian[i][3];
            int f2=ruanjian[i][4];
            int t=ruanjian[i][5];
            if((u&b1)!=b1||(u&b2)) continue;
            int j=u&(~f1)|f2;
            if(dis[j]>dis[u]+t){
                dis[j]=dis[u]+t;
                if(!in[j]) in[j]=1,q[++tail]=j;
            }
        }
    }
}

signed main(){
    scanf("%lld%lld",&n,&m);maxn=1<<n;
    for(int i=0;i<maxn;i++) dis[i]=1234567890;
    for(int h=1;h<=m;h++){
        scanf("%lld",&ruanjian[h][5]);
        scanf("%s%s",a,b);
        for(int i=0;i<n;i++){
            if(a[i]==‘+‘) ruanjian[h][1]|=1<<i;
            if(a[i]==‘-‘) ruanjian[h][2]|=1<<i;
            if(b[i]==‘-‘) ruanjian[h][3]|=1<<i;
            if(b[i]==‘+‘) ruanjian[h][4]|=1<<i;
        }
    }
    spfa(maxn-1);
    printf(dis[0]==1234567890?"0":"%lld",dis[0]);
    return 0;
}

原文地址:https://www.cnblogs.com/YoungNeal/p/8476591.html

时间: 2024-10-09 03:12:03

[Luogu P2761] 软件补丁问题的相关文章

P2761 软件补丁问题

P2761 软件补丁问题 思路 貌似不用网络流,直接状态压缩 用spfa跑最短路,直接判断是否能过 位运算太渣了,WA了好几发 代码 #include <bits/stdc++.h> using namespace std; const int N = 21, M = 101, inf = 0x3f3f3f3f; int read() { int x = 0, f = 1; char s = getchar(); for(; s > '9' || s < '0'; s = getc

洛谷 P2761 软件补丁问题 【spfa】

-为什么最短路的题会出现在网络流24里?? 因为范围是15所以直接把每个状态作为一个点,向它能转移到的点连有向边即可.可以不用建图(据说建图存不下?),直接枚举m个转移方案.位运算比较麻烦注意不要写错. #include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; const int N=1500005,inf=1e9; int n,m,d

P2761 软件补丁问题 【状压】【最短路】

输入输出样例 输入 #1复制 3 3 1 000 00- 1 00- 0-+ 2 0-- -++ 输出 #1复制 8 思路 为啥会有网络流的tag wa了5发感觉怎么建图都不对 一看都是状压最短路 打了个dijRE了 发现好像边太多 看别人都用的spfa莽,干脆我也莽一发好了 大家都知道spfa的核心就是边建图边松弛 如何建图: 因为对于第 i 个错误来说,只存在‘待修复’和‘已修复’两种状态 为了方便枚举,对状态进行压缩成一个整数来枚举 因为可能成环,显然状压dp很难处理这类问题 考虑用spf

软件补丁制作工具箱 v 1.0

想学习软件补丁制作的网友可以下载看看. https://pan.baidu.com/s/1c1T0j5I

【网络流24题】软件补丁问题(最短路)

[网络流24题]软件补丁问题(最短路) 题面 COGS 题解 这题貌似和网络流没啥关系 因为错误很少 可以直接状压 然后利用位运算直接跑最短路就行了 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map&

COGS439. [网络流24题] 软件补丁

[问题描述] 对于一个软件公司来说,在发行一个新软件之后,可以说已经完成了工作.但是实际上,许多软件公司在发行一个新产品之后,还经常发送补丁程序,修改原产品中的错误(当然,有些补丁是要收费的). 如某微硬公司就是这样的一个软件公司.今年夏天,在发行了一个新的字处理软件之后,到现在他们已经编写了许多补丁程序.仅仅在这个周末,他们就用新编写的补丁程序解决了软件中的一个大问题.而在每一个补丁程序修改软件中的某些错误时,有可能引起软件中原来存在的某些错误重新发作.发生这种情况是因为当修改一个错误时,补丁

【网络流24题】 No.12 软件补丁问题(最小转移代价 最短路)

[题意] T 公司发现其研制的一个软件中有 n 个错误, 随即为该软件发放了一批共 m 个补丁程序. 每一个补丁程序都有其特定的适用环境, 某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用.一个补丁在排除某些错误的同时, 往往会加入另一些错误.换句话说, 对于每一个补丁 i, 都有 2 个与之相应的错误集合 B1[i]和 B2[i],使得仅当软件包含 B1[i]中的所有错误, 而不包含 B2[i]中的任何错误时, 才可以使用补丁 i. 补丁 i 将修复软件中的某些错误 F1[

软件补丁问题(SPFA+位运算)

洛谷P2761 1.考虑到所有的错误只有"修复,未修复"两种情况,所以可以用0,1标记压缩状态,采用位运算减少时空浪费. 又考虑到有修复时间的关系,将时间抽象成边,将状态抽象为点(设修复为0,未修复为1)最后从(1<<n)-1开始寻找到0的最短路,SPFA一边建图一边松弛即可. 2.实现过程中,难点在于对二进制表示,以及位运算组合判断的处理. 首先,状态要表示(见前): 其次,补丁b,f要表示. 最初考虑用两个数来表示b,f,发现受"不动错误"影响,无法

[树形DP][背包]luogu P2515 软件安装

https://www.luogu.org/problemnew/show/P2515 分析 沙雕题*2 这题就是随便做个树形背包,Tarjan缩点把价值和体积加起来而已 注意必须保留当前节点的价值和体积 #include <iostream> #include <cstdio> #include <memory.h> using namespace std; const int N=110; const int M=510; struct Graph { int u,