大意:
有2*N把不同的锁,每把锁有一个钥匙,所以共有2*N 把钥匙。把2*N把钥匙两两配对共分为N组。
有个M层楼,每层楼有一个门,每个门上有两把锁,可能是相同的也可能是不同的。 走上某层楼之前,必须要打开这个门上的至少一个锁。
要你从每组钥匙中选择一把钥匙,然后用这些钥匙去上这栋楼,问最多能走到几层楼?
思路:
对于每个门的锁来说, 两把锁 a, b的关系是 a or b = 1, 对于钥匙来说就是 一对只能选一个,然后就简单了。
2n个钥匙,定义4n个节点,1~2n中的i表示用第i个钥匙。 2n+1~4n中的j, 表示不用j - 2n号钥匙。
那么对与给你的n组钥匙的每一组a和b。
有边<a, b + 2n> 和 <b, a + 2n>(只能选一个钥匙)
对于给你的m个门的两个锁a和b
有边<a + 2n, b> <b + 2n, a> 至少选一个。
可以这么理解, 选出n把钥匙 来打开这x个门, 通过门又对这些钥匙有一些限制,把对象变到钥匙就是个很裸的2-sat了
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e4 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt;
int k1[maxn], k2[maxn], d1[maxn], d2[maxn];
vector<int> v[maxn];
stack<int> s;
void init()
{
memset(low, 0, sizeof(low));
memset(id, 0, sizeof(id));
memset(dfn, 0, sizeof(dfn));
scc_cnt = dfs_cnt = 0;
for(int i = 0; i < maxn; i++)
v[i].clear();
while(!s.empty())
s.pop();
}
void addedge(int x, int y)
{
v[x].push_back(y);
}
void tarjan(int x)
{
dfn[x] = low[x] = ++dfs_cnt;
s.push(x);
for(int i = 0; i < v[x].size(); i++)
{
int to = v[x][i];
if(!dfn[to])
{
tarjan(to);
low[x] = min(low[x], low[to]);
}
else if(!id[to])
low[x] = min(low[x], dfn[to]);
}
if(low[x] == dfn[x])
{
scc_cnt++;
while(1)
{
int u = s.top();
s.pop();
id[u] = scc_cnt;
if(x == u) break;
}
}
}
void scc()
{
for(int i = 0; i < 2*n ; i++)
if(!dfn[i])
tarjan(i);
}
int check(int x)
{
init();
for(int i = 1; i <= n/2; i++)
addedge(k1[i], k2[i]+n), addedge(k2[i], k1[i]+n);
for(int i = 1; i <= x; i++)
{
if(d1[i] == d2[i])
addedge(d1[i]+n, d1[i]);
else
addedge(d1[i]+n, d2[i]), addedge(d2[i]+n, d1[i]);
}
scc();
for(int i = 0; i < n; i++)
if(id[i] == id[i+n])
return 0;
return 1;
}
int main()
{
while(~scanf("%d%d", &n, &m), n+m)
{
init();
for(int i = 1; i <= n; i++)
scanf("%d%d", &k1[i], &k2[i]);
for(int i = 1; i <= m; i++)
scanf("%d%d", &d1[i], &d2[i]);
n *= 2;
int l = 0, r = m, mid, ans = 0;
while(l <= r)
{
mid = (l+r)>>1;
if(check(mid))
ans = mid, l = mid + 1;
else
r = mid - 1;
}
printf("%d\n", ans);
}
return 0;
}