P2761 软件补丁问题
问题描述
某软件有\(n\)种 \(bug\) ,现有\(m\)种补丁,第\(i\)种补丁必须在有 \(B1[i]\)集合中的所有 \(bug\) 且没有\(B2[i]\)中任何一个 \(bug\) 时才能运行,效果是去除\(F1[i]\)集合中所有的 \(bug\) 但会新增\(F2[i]\) 集合中的所有bug,且会消耗\(T[i]\)的时间。求去除所有\(bug\)所需的最短时间,无解输出\(0\)。
输入格式
第 \(1\) 行有 \(2\) 个正整数 \(n\) 和 \(m,n\) 表示错误总数,\(m\) 表示补丁总数,\(1<=n<=20, 1<=m<=100\)。
接下来 \(m\) 行给出了 \(m\) 个补丁的信息。每行包括一个运行补丁 \(i\) 所需的时间,以及 \(2\) 个长度为 \(n\) 的字符串,中间用一个空格符隔开。
第 \(1\) 个字符串中,如果第 \(k\) 个字符为“+”,则表示第 \(k\) 个错误属于 \(B1[i]\),若为“-”,则表示第 \(k\) 个错误属于 \(B2[i]\),若为“0”,则第 \(k\) 个错误既不属于 \(B1[i]\) 也不属于 \(B2[i]\) ,即不影响补丁 i 的使用。
第 \(1\) 个字符串中,如果第 \(k\) 个字符为“+”,则表示第 \(k\) 个错误属于 \(F1[i]\),若为“-”,则表示第 \(k\) 个错误属于 \(F2[i]\),若为“0”,则第 \(k\) 个错误既不属于 \(F1[i]\) 也不属于 \(F2[i]\) ,即不会受补丁 \(i\) 影响而改变。
第 2 个字符串中,如果第 k 个字符 bk为“-”,则表示第 k 个错误属于 F1[i],若为“+”,则表示第 k 个错误属于 F2[i],若为“0”,则第 k 个错误既不属于 F1[i]也不属于 F2[i],即软件中是否包含第 k 个错误不会因使用补丁i 而改变。
输出格式
输出总耗时数(最小)。如果问题无解,则输出 0。
解析
可以把现在\(bug\)的状态压缩。用\(SPFA\)(或者\(Dijkstra\))找最短路
把每个补丁的B1,B2,F1,F2压缩成二进制字符串,把状态放进队列里寻找最短路,用t[i]来表示到达状态i所需要的时间,如果最后状态为0所需要的时间为初始化的0x3f就输出0,否则输出t[0]
代码如下
#include <bits/stdc++.h>
#define N 1<<21
#define inf 0x7f
using namespace std;
int m,n;
int h1[N],h2[N],c1[N],c2[N];
int len[105];
char s;
int t[N];
bool vis[N];
void spfa(){
memset(t,inf,sizeof(t));
queue<int> q;
t[(1<<n)-1]=0;q.push((1<<n)-1);
while (!q.empty()){
int top=q.front();q.pop();
vis[top]=0;
for (int i=1;i<=m;i++){
if (((top&h1[i])==h1[i])&&((top&h2[i])==0)){
int news=(((top|c1[i])^c1[i])|c2[i]);
if (t[top]+len[i]<t[news]){
t[news]=t[top]+len[i];
if (!vis[news]){
vis[news]=1;
q.push(news);}}}}}}
int main(){
scanf("%d%d",&n,&m);
for (int l=1;l<=m;l++){
scanf("%d",&len[l]);
scanf("%1c",&s);
for (int i=1;i<=n;i++){
scanf("%1c",&s);
if (s=='+')
h1[l]|=(1<<(i-1));
else if (s=='-')
h2[l]|=(1<<(i-1));
}
scanf("%1c",&s);
for (int i=1;i<=n;i++){
scanf("%1c",&s);
if (s=='+')
c2[l]|=(1<<(i-1));
else if (s=='-')
c1[l]|=(1<<(i-1));
}
}
spfa();
if (t[0]==2139062143) printf("0\n");//经过下载数据后输出的
else printf("%d",t[0]);
return 0;
}