http://poj.org/problem?id=2723
题意:
有2n把钥匙,分成2组,给你每组的钥匙信息,并且每组的钥匙只能用一个。
有m个门,每个门有2个锁,只要打开一个锁这个门就开了。(顺序遇见m个门)
问你最多能够打开多少个门。
思路:
因为门是按顺序打开的,所以用二分枚举能打开的门数,然后判断是否可行是比较好的选择。
建图主要从下面考虑: 对于第i扇门上的两把锁,要是两把锁同时打开,要么其中一把打开,
另外一把不能打开。即:(!a - > b) 和 ( !b -> a ) 。
代码:
#include<stdio.h> #include<string.h> const int MAXN = 1050*2 ; int N , M ; int x[1050] ,y[1050] ; int L1[3000] , L2[3000]; int fer[MAXN] , nn[MAXN]; int Gv[MAXN*MAXN] , Gnext[MAXN*MAXN] , Gr[MAXN] , Gc ; int dfn[MAXN] , low[MAXN] , stack[MAXN] , belong[MAXN] ; bool in[MAXN] ; int top, idx , Bcnt ; void add(int a, int b){ Gv[Gc] = b ; Gnext[Gc] = Gr[a] ; Gr[a] = Gc++ ; } void tarjin(int u){ int v ; low[u] = dfn[u] = ++idx ; stack[++top] = u ; in[u] = 1 ; for(int i=Gr[u] ;i!=-1;i=Gnext[i]){ v = Gv[i] ; if( !dfn[v] ){ tarjin(v); if( low[v] < low[u]) low[u] = low[v] ; } else if( in[v] && dfn[v] < low[u]) low[u] = dfn[v] ; } if( low[u] == dfn[u] ){ Bcnt ++ ; do{ v = stack[top--] ; in[v] = 0 ; belong[v] = Bcnt ; }while(u != v) ; } } bool solve(int m){ memset(Gr, -1, sizeof(Gr)) ; Gc = 0 ; for(int i=1;i<=m;i++){ int a = nn[fer[L1[i]]] ; int b = nn[L2[i]] ; add(a,b) ; a = nn[fer[L2[i]]] ; b = nn[L1[i]] ; add(a,b); } top = idx = Bcnt = 0 ; memset(dfn ,0 ,sizeof(dfn)); memset(in , 0 ,sizeof(in)); for(int i=1;i<=2*N;i++){ if( !dfn[i] ) tarjin(i); } for(int i=1;i<=N;i++){ if( belong[i] == belong[i+N] ) return false ; } return true ; } void deal(){ int l , h ,m ; l = 0 ; h = M ; while(l < h){ m = (l + h + 1) >> 1 ; if( solve(m) ){ l = m ; } else h = m - 1 ; } printf("%d\n",l); } int main(){ while(scanf("%d%d",&N,&M) == 2){ if(0==N && M==0) break ; for(int i=1;i<=N;i++){ scanf("%d%d",&x[i] ,&y[i]); fer[ x[i] ] = y[i] ; fer[ y[i] ] = x[i] ; nn[ x[i] ] = i ; nn[ y[i] ] = i + N ; } for(int i=1;i<=M;i++){ scanf("%d%d",&L1[i] , &L2[i]); } deal() ; } return 0 ; }