UVA658,隐式图+最短路+二进制子集枚举
来源:互联网 发布:jdbc连接数据库步骤 编辑:程序博客网 时间:2024/06/05 23:44
题目:
首先给出n和m,表示有n个bug和m个补丁。一开始存在n个bug,用1表示一个bug存在0表示不存在,所以一开始就是n个1,我们的目的是要消除所有的bug,所以目标状态就是n个0。对于每个补丁,会给出使用这个补丁的时间,另外会给出两个长度为n的字符串,第一个字符串表示这个补丁适用于什么情况下的bug,第二个字符串表示使用完这个补丁后原来的bug会变成怎么样。先说第一个字符串,s[i]=’0’,表示第i个bug存在与否都无所谓;s[i]=’+’,表示第i个bug一定要存在;s[i]=’-‘,表示第i个bug必须不存在;能不能使用这个补丁,就要看当前bug的状态是不是能不能全部满足第一个字符串,能的话就可以使用。第二个字符串表示使用完后的情况,ss[i]=’0’,表示第i个bug保持不变,原来是1就1是0就0;ss[i]=’+’,表示第i个bug必须为1;ss[i]=’-‘,表示第i个bug必须为0。
最终题目要求解的就是消除所有的bug并且用时最短,输出最短时间,如果bug不可能被完全消除那么就输出失败
思路:
可以看出是一个隐式图的最短路,但是因为可能在经过几个补丁后回到原本的状态,所以可能存在环,并不是DAG,所以动态规划无法解决,考虑使用Dijkstra或者SPFA:
第一次尝试:
需要考虑怎么来表示状态,因为最多只有二十个补丁,而且每个补丁存在与否的表示方法是‘-’或者‘+’,所以首先考虑string型字符串来储存每个状态。但是跑SPFA时候,数组d[maxn],inq[maxn]等无法直接用字符串来表示下标,所以考虑给状态进行标号。用vector<int>state 来保存当前所扩展出的所有状态,当扩展到一个新的状态v的时候,在state中查找,如果已经存在该状态直接用它的标号,否则将状态v加入state中并给它一个编号。代码如下
#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <map>#include <queue>#include <vector>using namespace std;const int maxn=25;const int maxm=100+10;const int INF=2147483647;int inq[1<<maxn],cnt[1<<maxn],d[1<<maxn];int tim[maxm];int n,m,kase;vector<string>state;vector<string>before;vector<string>after;bool judge(int St, int Pat){ string st=state[St],pat=before[Pat]; for(int i=0;i<n;i++){ if(st[i]!=pat[i]&&pat[i]!='0') return false; } return true;}int find(string st){ for(int i=0;i<state.size();i++){ if(st==state[i]) return i; } state.push_back(st); return state.size()-1;}//work函数有问题int work(int ST,int PAT){ string st=state[ST],pat=after[PAT],newst; for(int i=0;i<n;i++){ if(pat[i]=='0') newst.push_back(st[i]); else newst.push_back(pat[i]); } int num=find(newst); return num;}bool spfa(int s){ memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); for(int i=0;i<(1<<n);i++)d[i]=INF; queue<int>q; d[s]=0; q.push(s); inq[s]=1; while(!q.empty()){ int u=q.front();q.pop(); inq[u]=0; for(int i=0;i<m;i++){ int newst; if(judge(u,i)){ newst=work(u,i);//获得一个新的状态 if(d[u]<INF&&d[newst]>d[u]+tim[i]){ d[newst]=d[u]+tim[i]; if(!inq[newst]){ q.push(newst); inq[newst]=1; if(++cnt[newst]>(1<<n)) return false; } } } } } return true;}int main(){ freopen("in.txt","r",stdin); /*cin>>n; string s1,s2,s3; cin>>s1>>s2>>s3; state.push_back(s1); before.push_back(s2); after.push_back(s3); if(judge(0,0)){ cout<<"Yes"<<endl; //cout<<state[0]<<" "<<before[0]<<" "<<after[0]<<endl; int num=work(0,0); cout<<state[num]; }else cout<<"No"<<endl; */ kase=0; while(scanf("%d%d",&n,&m)&&n&&m){ kase++; cout<<"Product "<<kase<<endl; state.clear(); before.clear(); after.clear(); for(int i=0;i<m;i++){ cin>>tim[i]; string s1,s2; cin>>s1>>s2; before.push_back(s1); after.push_back(s2); } string ss,gs; for(int i=0;i<n;i++) ss.push_back('+'); for(int i=0;i<n;i++) gs.push_back('-'); state.push_back(ss); state.push_back(gs); int beg=0,en=1; if(spfa(beg)) cout<<"Fastest sequence takes "<<d[en]<<" seconds."<<endl; else cout<<"Bugs cannot be fixed."<<endl; }return 0;}
但是,上面的代码交上午会TLE,因为查找状态时最坏状态可能需要遍历所有的状态,会超时。
虽然上面代码无法AC,但是却在编写过程中遇到一个很有意思的问题,sting s1,s2;cin>>s1;for(int i=0;i<s1.length();i++)s2[i]=s1[i]后无法直接输出s2,而且在函数传递后也会出现问题,因为s2的内存没有被分配,只能使用s2.push_back(s1[i])或者s2+=s1[i]的方式来进行赋值。
LRJ在紫书给出的方法是,用一个n位的二进制串来表示当前状态,这样可以直接用这个二进制串来表示下标,且省略了查找状态这个步骤,可以直接跑dijkstra,LRJ大大的代码如下:
#include<cstdio>#include<cstring>#include<queue>using namespace std;struct Node { int bugs, dist; bool operator < (const Node& rhs) const { return dist > rhs.dist; }};const int maxn = 20;const int maxm = 100 + 5;const int INF = 1000000000;int n, m, t[maxm], dist[1<<maxn], mark[1<<maxn];char before[maxm][maxn + 5], after[maxm][maxn + 5];int solve() { for(int i = 0; i < (1<<n); i++) { mark[i] = 0; dist[i] = INF; } priority_queue<Node> q; Node start; start.dist = 0; start.bugs = (1<<n) - 1; q.push(start); dist[start.bugs] = 0; while(!q.empty()) { Node u = q.top(); q.pop(); if(u.bugs == 0) return u.dist; if(mark[u.bugs]) continue; mark[u.bugs] = 1; for(int i = 0; i < m; i++) { bool patchable = true; for(int j = 0; j < n; j++) { if(before[i][j] == '-' && (u.bugs & (1<<j))) { patchable = false; break; } if(before[i][j] == '+' && !(u.bugs & (1<<j))) { patchable = false; break; } } if(!patchable) continue; Node u2; u2.dist = u.dist + t[i]; u2.bugs = u.bugs; for(int j = 0; j < n; j++) { if(after[i][j] == '-') u2.bugs &= ~(1<<j); if(after[i][j] == '+') u2.bugs |= (1<<j); } int& D = dist[u2.bugs]; if(D < 0 || u2.dist < D) { D = u2.dist; q.push(u2); } } } return -1;}int main() { freopen("in.txt","r",stdin); int kase = 0; while(scanf("%d%d", &n, &m) == 2 && n) { for(int i = 0; i < m; i++) scanf("%d%s%s", &t[i], before[i], after[i]); int ans = solve(); printf("Product %d\n", ++kase); if(ans < 0) printf("Bugs cannot be fixed.\n\n"); else printf("Fastest sequence takes %d seconds.\n\n", ans); } return 0;}
表示完状态后基本上就是裸的dijkstra,按照这个思路我又写了一个SPFA,如下
#include <cstdio>#include <algorithm>#include <cstring>#include <iostream>#include <queue>using namespace std;const int maxn=25;const int maxm=100+5;const int INF=2147483647;int tim[maxm];char before[maxm][maxn],after[maxm][maxn];int beg,gol,kase=0;int n,m;int d[1<<maxn],inq[1<<maxn],cnt[1<<maxn];int spfa(int s){ memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); for(int i=0;i<(1<<n);i++)d[i]=INF; queue<int>q; q.push(s);d[s]=0;inq[s]=0; while(!q.empty()){ int u=q.front();q.pop(); inq[u]=0; for(int i=0;i<m;i++){ bool jud=1; for(int j=0;j<n;j++){ if(before[i][j]=='+'&&!(u&(1<<j))){ jud=0;break; } if(before[i][j]=='-'&&(u&(1<<j))){ jud=0;break; } } if(jud){ int v=u; for(int j=0;j<n;j++){ if(after[i][j]=='-')v=v&~(1<<j); if(after[i][j]=='+')v=v|(1<<j); } if(d[u]<INF&&d[v]>d[u]+tim[i]){ d[v]=d[u]+tim[i]; if(!inq[v]){ q.push(v); inq[v]=1; if(++cnt[v]>=(1<<n)) return false; } } } } } return true;}int main(){ // freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&m)==2&&n&&m){ kase++; for(int i=0;i<m;i++){ cin>>tim[i]; cin>>before[i]>>after[i]; } beg=(1<<n)-1; gol=0; cout<<"Product "<<kase<<endl; if(spfa(beg)&&d[gol]<INF){ cout<<"Fastest sequence takes "<<d[gol]<<" seconds."<<endl; cout<<""<<endl; }else{ cout<<"Bugs cannot be fixed."<<endl; cout<<""<<endl; } }return 0;}
有一个很细微的区别,在跑dijkstra时候,如果扩展时u结点便是目标结点,那么可以直接返回u的值。因为dij中,当从优先队列取出u结点是,v(i)->u的所有v都已经被扩展过了(有个永久编号done[)],既d[u]已经是最优值了,但是spfa不是这样(应该··不是这样···一会再试一下····)。
另一个关于二进制串的问题,原本在自己写的时候,这一句:if(after[i][j]=='-')v=v&~(1<<j),是说对于第j个bug变为没有bug,本来写的是v=v^(1<<j),但是这样写可以的前提是第j个bug存在,但这里有可能本来第j个bug就不存在,所以不能直接用“异或”。而是要用先对于j取反在取交集。
另。二进制集合表示:
ALL=(1<<n)-1是1到n-1的全集。
(1<<j)表示的是单个的元素j
- UVA658,隐式图+最短路+二进制子集枚举
- 神奇的最短路-uva658
- hdu6166-最短路&二进制枚举-Senior Pan
- 二进制枚举子集
- 二进制枚举集合子集
- 二进制枚举子集
- 二进制枚举子集
- 二进制-枚举子集
- C++二进制枚举子集
- 二进制枚举子集
- 二进制枚举子集
- 二进制枚举法枚举子集
- 使用二进制数枚举子集
- USACO Healthy Holsteins(二进制枚举子集)
- 二进制枚举子集与容斥
- hdu3986枚举+最短路
- hdu2363 枚举+最短路
- hdu2363 枚举最短路
- python 使用matplotlib,pylab进行python绘图
- python设计模式(访问者模式)
- 20171107 排版后台代码
- gnuplot 双纵坐标 对数坐标 坐标范围等
- Android 简单EventBus登录界面与传值(粘性事件)
- UVA658,隐式图+最短路+二进制子集枚举
- loj模拟赛t1
- 一行代码轻松搞定各种IE兼容问题,IE6,IE7,IE8,IE9,IE10
- PHAsset获得视频URL时,用requestAVAssetForVideo有时会得到AVComposition这个对象
- Linux经典问题—五哲学家就餐问题
- mkdocs完美支持sequence时序图(亲测可用)
- Java
- yum安装时报Another app is currently holding the yum lock; waiting for it to exit...
- 正则表达式常用通配符