【状压】软件补丁问题
软件补丁问题
不知道为什么放在网络流24题里面,我感觉是状压。
一个软件中有\(n\)个错误,共\(m\)个补丁程序。每一个补丁程序都有其特定的适用环境。对于每一个补丁\(i\)都有\(2\)个与之相应的错误集合\(B1[i]\)和,\(B2[i]\)使得仅当软件包含\(B1[i]\)中的所有错误,而不包含\(B2[i]\)中的任何错误时,才可以使用补丁\(i\)。补丁\(i\)将修复软件中的某些错误\(F1[i]\),而同时加入另一些错误\(F2[i]\)。每个补丁都耗费一定的时间。
试设计一个算法,利用\(m\)个补丁程序将原软件修复成一个没有错误的软件,并使修复后的软件耗时最少。对于给定的\(n\)个错误和\(m\)个补丁程序,找到总耗时最少的软件修复方案。
输入格式:
第 1 行有 2 个正整数\(n\)和\(m\),\(n\)表示错误总数,\(m\)表示补丁总数,\(1\leq n\leq20\), \(1\leq m\leq 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\)。
\(Solution\)
因为数据范围很小,所以我一开始想到的是状压。
状压的状态就是软件错误的状态。总共是\(2^{20}\)种,开一个数组\(dis\)表示到达某种状态的最短时间,那么最后的答案就是\(dis[0]\)(我以"\(1\)"为“有错误”)
首先,读入。
将\(B1\)和\(B2\)分开存。由题面可知,如果\(B1|x==x\),这说明\(x\)中包含\(B1\),若\(B2\)&\(x==0\),则说明\(B2\)和\(x\)没有交集。这样就可以判断某个修补程序是否可用。
而\(F\)的读入方法类似。两个分开存。
int x = now ^ (now & f1[i]);
x |= f2[i];
\(now\)是当前状态,\(x\)是使用修补程序后的状态。这个式子应该不难推。
剩下的就是跑最短路了。相当于在状态之间连边,“距离”为最小时间。
\(code\)
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
long long read(){
long long x = 0; int f = 0; char c = getchar();
while(c < '0' || c > '9') f |= c == '-', c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f? -x:x;
}
int n, m, S, t[105], b1[105], b2[105], f1[105], f2[105], dis[1<<21];
bool vis[1<<21];
bool judge(int x, int u){//判断程序是否可用
if((b1[u] | x) != x) return false;
if(b2[u] & x) return false;
return true;
}
void spfa(){
memset(dis, 0x3f3f3f3f, sizeof dis);
queue<int> q;
q.push(S), dis[S]=0;
while(!q.empty()){
int now = q.front(); q.pop();
for(int i = 1; i <= m; ++i){
if(!judge(now, i)) continue;
int x = now ^ (now & f1[i]);
x |= f2[i];//计算新的状态
if(dis[x] > dis[now] + t[i]){
dis[x] = dis[now] + t[i];
if(!vis[x]) q.push(x), vis[x] = 1;
}
}
vis[now] = 0;
}
}
string s;
int main(){
n = read(), m = read();
S = 1 << n, --S;
for(int i = 1; i <= m; ++i){
t[i] = read(), cin >> s;//读入
for(int j = 0; j < n; ++j)
if(s[j] == '+') b1[i] += 1 << j;
else if(s[j] == '-') b2[i] += 1 << j;
cin >> s;
for(int j = 0; j < n; ++j)
if(s[j] == '-') f1[i] += 1 << j;
else if(s[j] == '+') f2[i] += 1 << j;
}
spfa();
if(dis[0] == 0x3f3f3f3f) puts("0");//特判
else printf("%d", dis[0]);
return 0;
}