POJ 2723 Get Luffy Out【二分+2-sat】
题意:已知有 2*n把钥匙,这些钥匙两两一组,每组只能用其中的一把钥匙,有 m 个门,每个门上有两把锁,只要有一把锁被打开门就可以被打开,
一个门上可能是两把相同的锁,不同的门上也有可能有相同的锁,给出门的顺序,问最多可以打开多少扇门。
分析: 每种钥匙都有两种状态,用 or 不用,
kn = 2*n
i 表示 第 i 把钥匙被用,i + kn 表示 不用第 i 把钥匙,
在给出的要是分组中的 i , j
连边 i - > j + kn
j - > i + kn
表示 如果用 i 钥匙则 j 钥匙不能用,如果用 j 钥匙则 i 钥匙不能用
2 分枚举要开的前 k 个门,对于每个门上的两把锁 i , j
连边 i + kn - > j
j + kn - > i
表示如果不开锁 i ,则必须开锁 j,如果不开锁 j,则必须开锁 i,
如果枚举的k个门中没有冲突,则该情况符合。
#include<stdio.h> #include<string.h> #include<math.h> #define maxn 5000 #define clr(x)memset(x,0,sizeof(x)) #define min(a,b)(a)<(b)?(a):(b) struct node { int to,next; }e[1000000]; int tot; int head[maxn]; void add(int s,int t) { e[tot].to=t; e[tot].next=head[s]; head[s]=tot++; } int ti,sn,top; int dfn[maxn]; int low[maxn]; int ins[maxn]; int sta[maxn]; int col[maxn]; void tarjan(int u) { dfn[u]=low[u]=++ti; sta[++top]=u; ins[u]=1; int i,k; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(dfn[k]==0) { tarjan(k); low[u]=min(low[u],low[k]); } else if(ins[k]) low[u]=min(low[u],dfn[k]); } if(dfn[u]==low[u]) { sn++; do { k=sta[top--]; ins[k]=0; col[k]=sn; }while(k!=u); } } struct door { int lock1,lock2; }d[(1<<11)+10],key[maxn]; int kn,n; bool ok(int tm) { int i; int res=0; tot=1; clr(head); for(i=0;i<n;i++) { add(key[i].lock1,key[i].lock2+kn); add(key[i].lock2,key[i].lock1+kn); } for(i=1;i<=tm;i++) { add(d[i].lock1+kn,d[i].lock2); add(d[i].lock2+kn,d[i].lock1); } top=sn=ti=0; clr(dfn); for(i=0;i<kn;i++) if(!dfn[i]) tarjan(i); for(i=0;i<n;i++) if(col[i]==col[i+kn]) return 0; return 1; } int main() { int m; while(scanf("%d%d",&n,&m),n||m) { kn=n*2; int a,b,i,j; for(i=0;i<n;i++) scanf("%d%d",&key[i].lock1,&key[i].lock2); j=n; for(i=1;i<=m;i++) scanf("%d%d",&d[i].lock1,&d[i].lock2); int l=1,r=m; int mid; int res=0; while(l<=r) { mid=(l+r)>>1; if(ok(mid)) { res=mid; l=mid+1; } else r=mid-1; } printf("%d\n",res); } return 0; }