洛谷P2761 软件补丁问题 [状压DP,SPFA]
软件补丁问题
题目描述
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。
输入输出样例
说明
none!
分析:
说好的网络流二十四题???
反正想了很久想不到怎么搞网络流,然后一看。。。O.O???状压?SPFA?神奇。思路倒是不难,因为nn的范围只有20,所以就可以用二进制来表示当前的状态,转移的时候因为需要满足包含B1[]中的错误,不包含B2[]中的错误,那么我们也可以想到把它转化成一张图,用SPFA来转移。
一开始把输入的定义搞混调了好久,真SA。。。
Code:
- //It is made by HolseLee on 27th July 2018
- //Luogu.org P2761
- #include<cstdio>
- #include<cstring>
- #include<cstdlib>
- #include<cmath>
- #include<iostream>
- #include<iomanip>
- #include<algorithm>
- #include<queue>
- using namespace std;
- const int inf=0x3f3f3f3f;
- int n,m,a[101],has[101],dont[101],dp[(1<<21)],kill[101],bring[101];
- char s[2][21];
- bool vis[(1<<21)];
- queue<int>t;
- void ready(int x)
- {
- for(int i=n-1;i>=0;--i){
- switch (s[1][i]){
- case '+':has[x]|=(1<<i);break;
- case '-':dont[x]|=(1<<i);break;
- }
- switch (s[2][i]){
- case '+':bring[x]|=(1<<i);break;
- case '-':kill[x]|=(1<<i);break;
- }
- }
- }
- int main()
- {
- scanf("%d%d",&n,&m);
- for(int i=1;i<=m;++i){
- scanf("%d%s%s",&a[i],s[1],s[2]);
- ready(i);
- }
- memset(dp,inf,sizeof(dp));
- dp[(1<<n)-1]=0;vis[(1<<n)-1]=true;
- t.push(((1<<n)-1));
- while(!t.empty()){
- int now=t.front();t.pop();
- vis[now]=false;
- for(int i=1;i<=m;++i){
- if((has[i]&now)!=has[i])continue;
- if((dont[i]&(~now))!=dont[i])continue;
- int to=(now&(~kill[i]))|bring[i];
- if(dp[to]<=dp[now]+a[i])continue;
- dp[to]=dp[now]+a[i];
- if(!vis[to]){
- t.push(to);vis[to]=true;
- }
- }
- }
- if(dp[0]==inf)printf("0\n");
- else printf("%d\n",dp[0]);
- return 0;
- }
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)
· 程序员常用高效实用工具推荐,办公效率提升利器!