欢迎访问我的Uva题解目录 https://blog.csdn.net/richenyunqi/article/details/81149109
题目描述
题意解析
补丁在修正bug时,有时也会引入新的bug。假定有n(n≤20)个潜在bug和m(m≤100)个补丁,每个补丁用两个长度为n的字符串表示,其中字符串的每个位置表示一个bug。第一个串表示打补丁之前的状态(“-”表示该bug必须不存在,“+”表示必须存在,0表示无所谓),第二个串表示打补丁之后的状态(“-”表示不存在,“+”表示存在,0表示不变)。每个补丁都有一个执行时间,你的任务是用最少的时间把一个所有bug都存在的软件通过打补丁的方式变得没有bug。一个补丁可以打多次。
算法设计
可以用一个n位二进制串表示当前软件存在的bug的“状态”。以这样的状态作为结点,状态转移看成边,转化成图论中的最短路径问题,然后使用Dijkstra算法求解。不过这道题和普通的最短路径问题不一样:结点很多,多达个,而且很多状态根本遇不到(即不管怎么打补丁,也不可能打成那个状态),所以没有必要提前把图储存好。在Dijkstra算法求解过程中,当需要得到某个结点u出发的所有边时,不是去读G[u],而是直接枚举所有m个补丁,看看是否能打得上。
注意点
- 每个结点bug的状态不要用
string来存储,这样会相当耗时,可以用bitset来存储,bitset其实就是一串二进制位,关于它的用法可以参考bitset - C++ Reference。其实我们完全可以把bitset当作更高效的bool类型数组来使用。 - 本题中尽量使用
scanf读入,不要使用cin,因为本题测试数据量很大,cin读入非常耗时。
C++代码
#include<bits/stdc++.h>
using namespace std;
struct Patch{//补丁
int time;
char s1[25],s2[25];
};
typedef pair<int,int> pis;
bool match(const bitset<32>&b,const Patch&p){//当前状态与补丁是否匹配
for(int i=0;p.s1[i]!='\0';++i)
if((p.s1[i]=='+'&&!b[i])||(p.s1[i]=='-'&&b[i]))
return false;
return true;
}
int fix(bitset<32>&b,const Patch&p){//打补丁,返回打好补丁后的状态
for(int i=0;i<p.s2[i]!='\0';++i){
if(p.s2[i]=='+')
b[i]=true;
else if(p.s2[i]=='-')
b[i]=false;
}
return b.to_ulong();
}
int main(){
int n,m;
for(int ii=1;~scanf("%d%d",&n,&m)&&n!=0;++ii){
vector<Patch>patches(m);//存储所有的补丁
for(int i=0;i<m;++i)
scanf("%d%s%s",&patches[i].time,patches[i].s1,patches[i].s2);
unordered_map<int,int>dis={{(1<<n)-1,0}};//存储状态及对应花费的最短时间
priority_queue<pis,vector<pis>,greater<pis>>pq;
pq.push({0,(1<<n)-1});
while(!pq.empty()){//Dijkstra算法
pis t=pq.top();
pq.pop();
if(dis[t.second]!=t.first)
continue;
for(Patch p:patches){
bitset<32>b(t.second);
if(match(b,p)){
int s=fix(b,p);
if(dis.find(s)==dis.end()||dis[s]>t.first+p.time){
dis[s]=t.first+p.time;
pq.push({t.first+p.time,s});
}
}
}
}
printf("Product %d\n",ii);
if(dis.find(0)!=dis.end())
printf("Fastest sequence takes %d seconds.\n\n",dis[0]);
else
printf("Bugs cannot be fixed.\n\n");
}
return 0;
}