题目链接:http://poj.org/problem?id=2723
题意:给出 n 对钥匙,每对只能挑一把使用,每把只能用一次,也就是当一对钥匙中的一把被使用后,另一把也就不能再用了。给出 m 个门,每个门都有两把钥匙可以打开,问最多能开几道门(打开时必须按照顺序从前向后一个不落)。
思路:(1)n 对钥匙中,A 和 B 只能选择一把,用点 A 表示选择钥匙 A ,用 A’表示不选择,建边(A -> B‘)表示用钥匙 A 就不能用钥匙 B ;还有(B -> A' )表示用 B 就不能用 A;(2)m 道门,每对门都有两把钥匙可以开(假设是 C 和 D ),可能的选择是(不用 D 就必须用 C)或者(不用 C 就必须用D),根据这个关系建边(D' -> C),(C’-> D)。
#include <stdio.h>
#include <string.h>
#include <stack>
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
struct node
{
int v,next;
};
const int MAX=5005;
const int MAXE=13005;
node edges[MAXE];
int head[MAX],e;
int dfn[MAX],low[MAX],visit[MAX],color[MAX],col[MAX];
int n,m,index,cnt;
stack<int> S;
void Add(int u,int v)
{
edges[e].v=v;
edges[e].next=head[u];
head[u]=e++;
}
void Tarjan(int u)
{
int i,v;
low[u]=dfn[u]=++index;S.push(u);visit[u]=1;
for(i=head[u];i!=-1;i=edges[i].next)
{
v=edges[i].v;
if(!dfn[v])
{
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(visit[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
cnt++;
do
{
v=S.top();
S.pop();
visit[v]=0;
color[v]=cnt;
}while(u!=v);
}
}
int TWO_ST()
{
int i;
memset(dfn,0,sizeof(dfn));
memset(visit,0,sizeof(visit));
index=cnt=0;
while(!S.empty()) S.pop();
for(i=0;i<2*n;i++) if(!dfn[i]) Tarjan(i);
for(i=0;i<n;i++) if(color[i*2]==color[i*2+1]) return 0;
return 1;
}
int a[MAX][2],b[MAX][2];
inline int OK(int K)
{
if(!K) return 1;
return TWO_ST();
}
int main()
{
while(scanf("%d%d",&n,&m),n||m)
{
int i,ans;
for(i=1;i<=n;i++) scanf("%d%d",&b[i][0],&b[i][1]);
for(i=1;i<=m;i++) scanf("%d%d",&a[i][0],&a[i][1]);
int low=0,high=m,mid;
while(low<=high)
{
mid=(low+high)>>1;
memset(head,-1,sizeof(head));
e=0;
for(i=1;i<=n;i++)
{
Add(2*b[i][0],2*b[i][1]+1);
Add(2*b[i][1],2*b[i][0]+1);
}
for(i=1;i<=mid;i++)
{
Add(2*a[i][0]+1,2*a[i][1]);
Add(2*a[i][1]+1,2*a[i][0]);
}
if(OK(mid)) ans=mid,low=mid+1;
else high=mid-1;
}
printf("%d\n",ans);
}
return 0;
}