【洛谷P2761】软件补丁问题——杨子曰题目
喂喂喂,大标题和文章标题不一样!!
呵呵,同一道题……(就是如此神奇)
这里贴的是【洛谷P2761】软件补丁问题,其实就是uva那道题的翻译:
题目描述
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。
输入样例:
3 3
1 000 00-
1 00- 0-+
2 0-- -++
输出样例:
8
很多人说没看懂样例,好心的我来告诉大家样例是怎么回事:
一开始全是BUG:X X X
只能用1补丁:X X O
然后用2:X O X
再用一次1:X O O
用3:O X X
又只能用1:O X O
再用2:O O X
最后再来一次1:O O O
完事
洛谷居然把这道题放在网络流24题里,真不知道它于网络流的关系在何处,误导?
经过随便仔细地思考后,你会发现对于某一个状态可以用的补丁是可以确定的,也就是说它之后可以变成的状态我们是可以枚举出来的,而且我们还知道转移的代价,而且我们还知道初始状态(全是BUG)和终止状态(没有BUG)
你有没有想到什么???
噢噢噢噢~~~~~~~~~~~~~~~~~~~~~~~~对对对!!!
(神马玩意,我完全不知道……)
有没有发现这是一个最短路问题!
这张图的节点就是对于每个BUG有没有修好所构成的2^n 个状态,至于边,就是某个状态和能通过补丁转移成的另一个状态间有一条单向边,而边权就是修复的时间,注意哦,这些点,这些边是不用真的建出来的,我们直接枚举状态之间的转移,哈哈,最短路一搞,美滋滋(✿◕‿◕✿)
别高兴太早了,你还需要考虑这2^n 个状态以一种什么方式保存下来——一定有大佬想到了:对于每个BUG都是修好和被修好两个状态,所以如果我们用1/0表示,就可以状压成一个数字,完美!现在可以高兴了!!
啦啦啦,什么,TLE!!!!!没错,毕竟人家有2 ^20 个点,而且它还是一个稠密图。So,严重推荐使用堆优化Dijkstra
OK,完事
UVA658代码:
#include<bits/stdc++.h>
#define inf 2000000000
using namespace std;
int n,m;
int dist[2000005],vis[2000005],w[105];
char c[105][25],r[105][25];
struct node{
int d,u;
};
priority_queue<node> q;
bool operator < (node a,node b){
return a.d>b.d;
}
int check(int u,int k){
int o=u;
for (int i=n-1;i>=0;i--){
if (u%2==1 && c[k][i]=='-') return -1;
if (u%2==0 && c[k][i]=='+') return -1;
u>>=1;
}
int res=0;
for (int i=n-1;i>=0;i--){
if (r[k][i]=='+' || (r[k][i]=='0' && o%2==1)){
res+=(1<<(n-i-1));
}
o>>=1;
}
return res;
}
void dijkstra(){
memset(vis,0,sizeof(vis));
for (int i=0;i<(1<<n);i++) dist[i]=inf;
dist[(1<<n)-1]=0;
q.push((node){0,(1<<n)-1});
while(!q.empty()){
int u=q.top().u;
if (u==0) return;
q.pop();
if (vis[u]) continue;
vis[u]=1;
for (int i=0;i<m;i++){
int v=check(u,i);
if(v!=-1 && !vis[v] && dist[v]>dist[u]+w[i]){
dist[v]=dist[u]+w[i];
q.push((node){dist[v],v});
}
}
}
}
int main(){
int cas=0;
scanf("%d%d",&n,&m);
while(n!=0 || m!=0){
while(!q.empty()) q.pop();
for (int j=0;j<m;j++){
scanf("%d",&w[j]);
char ch;
scanf("%c",&ch);
for (int i=0;i<n;i++){
scanf("%c",&c[j][i]);
}
scanf("%c",&ch);
for (int i=0;i<n;i++){
scanf("%c",&r[j][i]);
}
scanf("%c",&ch);
}
dijkstra();
cout<<"Product "<<++cas<<endl;
if (dist[0]==inf) cout<<"Bugs cannot be fixed."<<endl<<endl;
else cout<<"Fastest sequence takes "<<dist[0]<<" seconds."<<endl<<endl;
scanf("%d%d",&n,&m);
}
return 0;
}
于HG机房