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 ;
}