POJ-2723(2-SAT)

28 篇文章 0 订阅
5 篇文章 0 订阅

题目: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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值