题意:给你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; }