题意:给你2n把钥匙,分成n组,每组只能选一把,然后另一把就不能用了。同时,有m个门,每个门上有两把锁,打开一个就可以通过,问最多可以通过几扇门;
思路:2-sat,假如(a,b)是同一组的钥匙,那么就有 a--b',b--a';
(c,d)是同一门上的锁那么就有d'--c,c'--d;(为什么不是c--d',d--c'? 因为2-SAT其实就是找不相容的点对,你开了c,当然也可以开d,并不能得到d',所以c,d是相容的,但是不开d,就只能开c)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
const int N = 2000*4;
vector<int> vec[N];
int n, m, id, cnt;
int dfn[N], vis[N], low[N], belong[N],key1[N],key2[N],door1[N],door2[N];
stack<int> s;
void init(){
memset(vis,0,sizeof(vis));
memset(dfn,-1,sizeof(dfn));
memset(low,-1,sizeof(low));
memset(belong,-1,sizeof(belong));
id = cnt = 0;
while(!s.empty())s.pop();
for(int i = 0; i < 4*n; i++)vec[i].clear();
}
void tarjan(int u){
dfn[u] = low[u] = id++;
vis[u] = 1;
int sz = vec[u].size();
s.push(u);
for(int i = 0; i < sz; i++){
int v = vec[u][i];
if(dfn[v] == -1){
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v] == 1){
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]){
cnt++;
while(!s.empty()){
int temp = s.top();
s.pop();
vis[temp]=0;
belong[temp]=cnt;
if(temp == u)break;
}
}
}
bool judge(int x){
init();
for(int i = 0; i < n; i++){
vec[key1[i]*2].push_back(key2[i]*2+1);
vec[key2[i]*2].push_back(key1[i]*2+1);
}
for(int i = 0; i < x; i++){
vec[door1[i]*2+1].push_back(door2[i]*2);
vec[door2[i]*2+1].push_back(door1[i]*2);
}
for(int i = 0; i < 4*n; i++){
if(dfn[i] == -1)tarjan(i);
}
for(int i = 0; i < 2*n; i++){
if(belong[i*2] == belong[i*2+1])return false;
}
return true;
}
int main(){
while(~scanf("%d%d",&n, &m),(n || m)){
for(int i = 0; i < n; i++){
scanf("%d%d",key1+i,key2+i);
}
for(int i = 0; i < m; i++){
scanf("%d%d",door1+i,door2+i);
}
int l = 0,r = m,ret;
while(l <= r){
int mid = (l+r)>>1;
if(judge(mid))ret = mid,l = mid+1;
else r = mid -1;
}
printf("%d\n",ret);
}
return 0;
}