HDU 1816 Get Luffy Out *(2-SAT)
http://acm.hdu.edu.cn/showproblem.php?pid=1816
题意:
你有2N把钥匙,你的前面按顺序有M个门,每个门有两个锁,现在你必须用这2N把钥匙去开门. 一个门只要一把锁被打开了这个门就可以被打开.且2N把钥匙还有N个配对关系,每一对的两把钥匙i与j,如果用了i钥匙,那么j钥匙永远不能再用.如果用了j钥匙,那么i钥匙永远不能在用.(这里一把钥匙i可能属于多个配对关系,而POJ2723中2N把钥匙是正好被分成了N对,每把钥匙只出现1次)
分析: 本题类似POJ2723:
http://blog.csdn.net/u013480600/article/details/34891151
由于POJ我没用二分做,所以这里我用二分再做一次.
对于2-SAT的图,有两类关系我们需要添加边.
第一类,对于a与b钥匙配对,那么a与b不能同时出现,有:
add(a,0,b,1) add(b,0,a,1) 这里a=0 表示用a钥匙.
第二类,对于门上有锁a与b,那么有:
add(a,1,b,0) add(b,1,a,0) a=1 表示不用钥匙a
其实该题直接用POJ2723的代码就能AC,刘汝佳的模板不用修改,可能用tarjan算法需要修改些东西吧?
AC代码: 二分125ms,比直接遍历速度(406ms)要快不少
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int maxn=2000+50; int n,m; int a[maxn],b[maxn];//每对钥匙 int c[maxn],d[maxn];//每个门上的锁对 struct TwoSAT { int n; vector<int> G[maxn*2]; int S[maxn*2],c; bool mark[maxn*2]; bool dfs(int x) { if(mark[x^1]) return false; if(mark[x]) return true; mark[x]=true; S[c++]=x; for(int i=0;i<G[x].size();i++) if(!dfs(G[x][i])) return false; return true; } void init(int n) { this->n=n; for(int i=0;i<2*n;i++) G[i].clear(); memset(mark,0,sizeof(mark)); } void add_clause(int x,int xval,int y,int yval) { x=x*2+xval; y=y*2+yval; G[x].push_back(y); } bool solve() { for(int i=0;i<2*n;i+=2) if(!mark[i] && !mark[i+1]) { c=0; if(!dfs(i)) { while(c>0) mark[S[--c]]=false; if(!dfs(i+1)) return false; } } return true; } }TS; bool ok(int mid) { TS.init(n); for(int i=0;i<n/2;i++) { TS.add_clause(a[i],0,b[i],1); TS.add_clause(b[i],0,a[i],1); } for(int i=0;i<mid;i++) //注意这里是i<mid,而不是i<m { TS.add_clause(c[i],1,d[i],0); TS.add_clause(d[i],1,c[i],0); } return TS.solve(); } int main() { while(scanf("%d%d",&n,&m)==2&&n) { n*=2; for(int i=0;i<n/2;i++) scanf("%d%d",&a[i],&b[i]); for(int i=0;i<m;i++) scanf("%d%d",&c[i],&d[i]); int L=0, R=m; while(R>L) { int mid = L+(R-L+1)/2; if(ok(mid)) L=mid; else R=mid-1; } printf("%d\n",L); } return 0; }