hdu1816 Get Luffy Out *--二分 & 2-sat

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=1816


题意:有m层楼,从一层到m层,要进入每层都要打开位于该层的两道门中的至少一道。门锁有2n种,每个门锁为2n种中的一种,可以重复。有2n把钥匙,分别对应2n种锁,但是钥匙两两一组,共n组,每组只能选一个来开门,被选中的可以多次使用,另一个一次都不能用。问最多能上多少层 。


分析:

首先对钥匙连线,两把钥匙a和b,如果选a那就不能选b,反过来一样。这个地方注意,我们是否需要连线如果不选a就选b这条线呢,是不需要的,因为这组钥匙可能根本用不到。

再对门连线,开门的钥匙a和b,如果没法用钥匙a,那就必须用钥匙b,反过来一样。同样需要注意,如果用了钥匙a,那么可不可以连线用b和不用b这条线呢,不需要的,因为对于一层来说只要开了一个门就可以。


#define _CRT_SECURE_NO_DEPRECATE 

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
#include<cmath>
#define INF 99999999
#define eps 0.0001
#define N ((1<<12)+10)
using namespace std;

int n, m;
int cnt, index;
int low[N];
int dfn[N];
int belong[N];
bool inStack[N];
vector<int> vec[N];
stack<int> s;
int key1[(1 << 10) + 10], key2[(1 << 10) + 10];
int door1[(1 << 11) + 10], door2[(1 << 11) + 10];

void init()
{
	cnt = index = 0;
	for (int i = 0; i < 4 * n; i++)
	{
		vec[i].clear();
		inStack[i] = 0;
		dfn[i] = 0;
	}
}

void build(int level)
{
	init();
	for (int i = 0; i < n; i++)
	{
		vec[key1[i]].push_back(key2[i] + 2 * n);
		vec[key2[i]].push_back(key1[i] + 2 * n);
	}

	for (int i = 0; i < level; i++)
	{
		vec[door1[i] + 2 * n].push_back(door2[i]);
		vec[door2[i] + 2 * n].push_back(door1[i]);
	}
}

void tarjan(int u)
{
	low[u] = dfn[u] = ++index;
	inStack[u] = 1;
	s.push(u); 
	int v;
	for (int i = 0; i < vec[u].size(); i++)
	{
		v = vec[u][i];
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (inStack[v])
			low[u] = min(low[u], dfn[v]);
	}

	if (low[u] == dfn[u])
	{
		cnt++;
		do
		{
			v = s.top();
			s.pop();
			belong[v] = cnt;
			inStack[v] = 0;
		} while (u != v);
	}
}

bool solve()
{
	for (int i = 0; i < 4 * n; i++)
		if (!dfn[i])
			tarjan(i);
	for (int i = 0; i < 2 * n; i++)
		if (belong[i] == belong[i + 2 * n])
			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;
		int r = m;
		int mid;
		while (l <= r)
		{
			mid = (l + r) / 2;
			build(mid);
			if (solve())
				l = mid + 1;
			else
				r = mid - 1;
		}

		printf("%d\n", l - 1);
	}
	return 0;
}






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值