题意:某人要去一个地方救朋友。已知他有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; }