poj 2723 Get Luffy Out

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

分析:二分查找能上的层数。每次对于一个确定的层数,也就确定了哪些门需要开。变为一个2-sat问题。其中两两一组的钥匙就是图中的节点。当然图中还需要一些矛盾。矛盾如下,各组的两个钥匙相矛盾(这里无需建边);每层开门钥匙的选择关系里,(first_key && second_key = 0 )

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 11000;
const int inf = 1000000000;
vector<int>edge[maxn];
int n, m, match[maxn], s[maxn], e[maxn];
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
void tarjan( int u )
{
	int i, v, t, size;
	low[u] = dfn[u] = tmpdfn++;
	st[top++] = u;
	inst[u] = 1;
	size = edge[u].size();
	for( i = 0; i < size; i++ )
	{
		v = edge[u][i];
		if( dfn[v] == -1 )
		{
			tarjan( v );
			low[u] = min( low[u], low[v] );
		}
		else if( inst[v] )low[u] = min( low[u], dfn[v] );
	}
	if( dfn[u] == low[u] )
	{
		do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
		scnt++;
	}
}
bool SCC()
{
	int i;
	top = 0;
	tmpdfn = scnt = 1;
	memset( dfn, -1, sizeof(dfn) );
	memset( inst, 0, sizeof(inst) );
	for( i = 1; i <= n * 2; i++ )if( dfn[i] == -1 )tarjan( i );
	for( i = 1; i <= n; i++ )if( belong[i] == belong[match[i]] )return false;
	return true;
}
void build( int f )
{
	int i, j;
	for( i = 1; i <= n * 2; i++ )edge[i].clear();
	for( i = 1; i <= f; i++ )
	{	// it can choose one key each pair but none 
		edge[s[i]].push_back( match[e[i]] );
		edge[e[i]].push_back( match[s[i]] );
	}
}
int main()
{
	int i, a, b;
	while( ~scanf( "%d%d", &n, &m ), n + m )
	{
		for( i = 1; i <= n; i++ )
		{
			scanf( "%d%d", &a, &b );
			a++;	b++;
			match[a] = b; 
			match[b] = a;
		}
		for( i = 1; i <= m; i++ )
		{
			scanf( "%d%d", &s[i], &e[i] );
			s[i]++;	e[i]++;
		}
		int l, r, mid;		// 二分查找
		l = 0;	r = m + 1;	// 1 and m are boundary.
		while( l + 1 != r )
		{
			mid = l + ( r - l ) / 2;
			build( mid );
			if( SCC() )
				l = mid;
			else
				r = mid;
		}
		printf( "%d\n", l );
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值