题意:某人要去一个地方救朋友。已知他有n对钥匙,一共2n把。要求每对钥匙只能选一把来用。有m个门,每个门有2把锁,只要打开其中的一把锁就能打开这个门。见到门的顺序和输入一致。给定钥匙信息以及每扇门上的两把锁的信息,问如何选择钥匙能够打开最多的门。
思路:因为有2n把不同钥匙,所以图的节点有4n个。如果输入的钥匙信息为(a,b),那么添加边a->b+2n;b->a+2n。表示如果带上了a钥匙,那么一定不能带b;反之亦然;
对于给出的门的信息(a,b)这样添加边:a+2n->b;b+2n->a。表示如果没有带上a钥匙,那么一定要带上b钥匙;反之亦然。接着就是建图tarjan,二分答案……
#include <stdio.h>
#include <string.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define clc(a) memset(a,0,sizeof(a))
#define N 1030
#define M 2050
struct pair{
int a,b;
}key[N],door[M];
struct edge{
int y,next;
}e[(N+M)<<1];
int first[4*N],strong[4*N],dfn[4*N],low[4*N],stack[4*N];
int n,m,top,tops,id,num;
void init(){
top = id = num = 0;
tops = -1;
clc(strong);
memset(first,-1,sizeof(first));
memset(dfn,-1,sizeof(dfn));
}
void add(int x,int y){
e[top].y = y;
e[top].next = first[x];
first[x] = top++;
}
void tarjan(int x){
int i,y;
dfn[x] = low[x] = ++id;
stack[++tops] = x;
for(i = first[x];i!=-1;i=e[i].next){
y = e[i].y;
if(dfn[y] == -1){
tarjan(y);
low[x] = min(low[x],low[y]);
}else if(!strong[y])
low[x] = min(low[x],dfn[y]);
}
if(dfn[x] == low[x]){
num++;
do{
strong[stack[tops]] = num;
}while(stack[tops--]!=x);
}
}
int main(){
freopen("a.txt","r",stdin);
while(scanf("%d %d",&n,&m)&&n&&m){
int i,lo,high,mid,res;
for(i = 0;i<n;i++)
scanf("%d %d",&key[i].a,&key[i].b);
for(i = 0;i<m;i++)
scanf("%d %d",&door[i].a,&door[i].b);
lo = 1;
high = m;
while(lo <= high){
mid = (lo+high)>>1;
init();
for(i = 0;i<n;i++){
add(key[i].a,key[i].b+2*n);
add(key[i].b,key[i].a+2*n);
}
for(i = 0;i<mid;i++){
add(door[i].a+2*n,door[i].b);
add(door[i].b+2*n,door[i].a);
}
for(i = 0;i<4*n;i++)
if(dfn[i] == -1)
tarjan(i);
for(i = 0;i<2*n;i++)
if(strong[i] == strong[i+2*n])
break;
if(i >= 2*n){
res = mid;
lo = mid+1;
}else
high = mid-1;
}
printf("%d\n",res);
}
return 0;
}