题目:http://poj.org/problem?id=2723
第5道2-SAT,一开始没想起来怎么构造,然后想到了用mark[2i]表示是否开gate[i]的左边一把锁lock[2i],用mark[2i+1]表示是否开gate[i]的右边一把锁lock[2i+1],则如果lock[x]和lock[y]需要的钥匙不同且lock[x]和lock[y]需要的钥匙在同一串上(一串钥匙有两个),则构成条件开lock[x]或者开lock[y],注意到开锁要求必须从gate[0]到gate[i-1]都开完了才能开gate[i]上的锁,因此我们可以通过二分搜索满足要求的最大开门数量,毕竟如果能开N扇门,则一定能开N-1扇门。
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAX_N = 1 << 10;
const int MAX_M = 1 << 11;
int N, keyPair[MAX_N * 2];
int M, lockKey[MAX_M * 2];
bool mark[MAX_M * 2];//for gate i, mark[2i] = true => open lock 2i; mark[2i+1] = true => open lock 2i+1
vector<int> out[MAX_M * 2];
int stack[MAX_M * 2], cnt;
void init(int m)
{
memset(mark, 0, 2*m);
for(int i = 2*m-1; i > -1; --i) out[i].clear();
}
void build(int m)
{
int i, j, a, b;
for(i = 0; i < m; ++i){
//2i是gate i的第一把锁
a = lockKey[2 * i];
for(j = i-1; j > -1; --j){
//2j是gate j的第一把锁
b = lockKey[2 * j];
if(b != a && keyPair[b] == keyPair[a]){//lock 2i和lock 2j不能同时开
//要是开2i就不能开2j,反之亦然
out[2 * i].push_back(2 * j + 1);
out[2 * j].push_back(2 * i + 1);
}
//2j+1是gate j的第二把锁
b = lockKey[2 * j + 1];
if(b != a && keyPair[b] == keyPair[a]){//lock 2i和lock 2j+1不能同时开
//要是开2i就不能开2j+1,反之亦然
out[2 * i].push_back(2 * j);
out[2 * j + 1].push_back(2 * i + 1);
}
}
//2i+1是gate i的第二把锁
a = lockKey[2 * i + 1];
for(j = i-1; j > -1; --j){
//2j是gate j的第一把锁
b = lockKey[2 * j];
if(b != a && keyPair[b] == keyPair[a]){//lock 2i+1和lock 2j不能同时开
//要是开2i+1就不能开2j,反之亦然
out[2 * i + 1].push_back(2 * j + 1);
out[2 * j].push_back(2 * i);
}
//2j+1是gate j的第二把锁
b = lockKey[2 * j + 1];
if(b != a && keyPair[b] == keyPair[a]){//lock 2i+1和lock 2j+1不能同时开
//要是开2i+1就不能开2j+1,反之亦然
out[2 * i + 1].push_back(2 * j);
out[2 * j + 1].push_back(2 * i);
}
}
}
}
bool dfs(int x)
{
if(mark[x ^ 1]) return false;
if(mark[x]) return true;
mark[x] = true;
stack[cnt++] = x;
vector<int>& v = out[x];
for(int i = v.size() - 1; i > -1; --i){
if(!dfs(v[i])) return false;
}
return true;
}
bool test(int m)
{
init(m);
build(m);
for(int i = 0; i < m; ++i){
cnt = 0;
if(!dfs(i << 1)){
while(cnt) mark[stack[--cnt]] = false;
if(!dfs(i << 1 | 1)) return false;
}
}
return true;
}
int main()
{
int i, k, l, m, r;
while(scanf("%d%d", &N, &M), N){
//input
for(i = 0; i < N; ++i){
scanf("%d%d", &l, &r);
keyPair[l] = keyPair[r] = i;
}
k = 0;
for(i = 0; i < M; ++i){
scanf("%d%d", lockKey + k, lockKey + k + 1);
k += 2;
}
//binary search
l = 1; r = M + 1;
while(l + 1 < r){
m = (l + r) >> 1;
if(test(m)) l = m;
else r = m;
}
printf("%d\n", l);
}
return 0;
}