洛谷 P2761 软件补丁问题
题目描述
T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序。每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用。一个补丁在排除某些错误的同时,往往会加入另一些错误。
换句话说,对于每一个补丁 i,都有 2 个与之相应的错误集合 B1[i]和 B2[i],使得仅当软件包含 B1[i]中的所有错误,而不包含 B2[i]中的任何错误时,才可以使用补丁 i。补丁 i 将修复软件中的某些错误 F1[i],而同时加入另一些错误 F2[i]。另外,每个补丁都耗费一定的时间。
试设计一个算法,利用 T 公司提供的 m 个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的 n 个错误和 m 个补丁程序,找到总耗时最少的软件修复方案。
1<=n<=20, 1<=m<=100
问题分析:
由于n的范围很小,我们可以把每一个漏洞的状态用0和1来表示,即将当前系统状态用一个01的串来表示,状态压缩。
DP即可,但是由于DP状态会有1e6种,每种转移要m次,复杂度太大了。。。所以用最短路解决问题,可以减少一些不会到达的状态。
起点为所有漏洞都未被修复的状态(11.....11),终点为所有漏洞都已被修复的状态(00....00).
代码:
#include <bits/stdc++.h> using namespace std; char s[25]; int t[105]; int sta1[105],sta2[105]; int res1[105],res2[105]; int dis[2100000],vis[2100000]; queue<int> Q; int main() { memset(dis,0x3f,sizeof(dis)); int n,m; scanf("%d%d",&n,&m); for(int i = 1;i <= m;++i){ scanf("%d",&t[i]); scanf("%s",s); for(int j = 0;j < n;++j){ if(s[j] == '+') sta1[i] += (1<<j); else if(s[j] == '-') sta2[i] += (1<<j); } scanf("%s",s); for(int j = 0;j < n;++j){ if(s[j] == '+') res2[i] += (1<<j); else if(s[j] == '-') res1[i] += (1<<j); } } int now = (1<<n)-1; dis[now] = 0; Q.push(now); vis[now] = 1; while(!Q.empty()){ now = Q.front(); Q.pop(); for(int i = 1;i <= m;++i){ if((now & sta1[i]) != sta1[i]) continue; if((~now & sta2[i]) != sta2[i]) continue; int tmp = (now & (~res1[i])) | res2[i]; if(dis[tmp] > dis[now] + t[i]){ dis[tmp] = dis[now] + t[i]; if(!vis[tmp]) vis[tmp] = 1,Q.push(tmp); } } vis[now] = 0; } if(dis[0] == 0x3f3f3f3f) puts("0"); else printf("%d\n",dis[0]); return 0; }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步