您的位置:首页 > 其它

Poj 2723 Get Luffy Out (2-SAT + 二分)

2014-08-31 22:55 411 查看
参考了:POJ 2723 Get Luffy Out(2-SAT + 二分) - D_Double's Journey

题意:有2*N把不同的锁,每把锁有一个钥匙,共有2*N 把钥匙。把2*N把钥匙两两配对共分为N组。

有个M层楼,每层楼有一个门,每个门上有两把锁,可能是相同的也可能是不同的。 走上某层楼之前,必须要打开这个门上的至少一个锁。

从每组钥匙中只能选择一把钥匙,然后用这些钥匙去开门上楼,问最够能到几层?

思路:因为门只能按照顺序打开,所以用二分法将问题转化为判定问题。

对于一个门上的两把锁,如果这两把锁的钥匙是同一组的,那么任选一个钥匙就可以。

如果是属于不同组的,那么假设这两把钥匙是a1, b1,那么这两组分别是(a1, a2) (b1, b2), a2和b2是一定不能同时选的,因为选了就没有a1或b1来打开这个门了

所以<a2,b2>是一个矛盾对,加入边a1->b2, b2->a1

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

const int MAXN = 2100;
const int VN   = MAXN*2;
const int EN   = VN;
int n,m;

struct Edge{
	int v, next;
};

class Graph{
public:
	int head[VN],size;
	Edge E[EN];
	void init(){
		size = 0;
		memset(head, -1, sizeof(head));
	}
	void Add (int u,int v){
		E[size].v = v;
		E[size].next = head[u];
		head[u] = size++;
	}
}g;

class Two_Sat{
public:
	bool Check(const Graph&g, const int n){
		SCC(g,n);//传入几对点
		for (int i=0;i<n;i++)
			if (belong[i*2] == belong[i*2+1])
				return false;
		return true;
	}
private:
	int top, bcnt, idx;
	int sta[VN],belong[VN];
	int dfn[VN],low[VN];
	bool inStack[VN];
	
	void targan (const Graph&g, const int u){
		int v;
		dfn[u] = low[u] = ++idx;
		sta[top++] = u;
		inStack[u] = true;
		
		for (int i=g.head[u]; i!=-1; i=g.E[i].next){
			v = g.E[i].v;
			if (dfn[v] < 0){
				targan(g, v);
				low[u] = min(low[u], low[v]);
			}
			else if(inStack[v])
				low[u] = min(low[u], dfn[v]);
		}
		
		if (dfn[u] == low[u]){
			++bcnt;
			do{
				v = sta[--top];
				inStack[v] = false;
				belong[v] = bcnt;
			}while(u != v);
		}
	}

	void SCC (const Graph&g, int n){
		top=bcnt=idx=0;
		memset(dfn,-1,sizeof(dfn));
		memset(low,0,sizeof(low));
		memset(belong,0,sizeof(belong));
		memset(inStack,false,sizeof(inStack));
		for (int i=0; i<2*n;i++)
			if (dfn[i] < 0) targan(g,i);
	}
}sat;

int key[VN];
int door[MAXN][2];
int idx[VN];

int main ()
{
	int i;
	while (scanf("%d%d",&n,&m),n+m)
	{
		for (i=0;i<2*n;i++)
		{
			scanf("%d",&key[i]);
			idx[key[i]]=i;
		}
		for (i=0;i<m;i++)
			scanf("%d%d",&door[i][0],&door[i][1]);

		int low=0,high=m,mid;
		int ans=0;
		while (low<=high)
		{
			mid=(low+high)>>1;
			g.init();
			for (i=0;i<mid;i++)
			{
				int a1=door[i][0], b1=door[i][1];
				int a2=key[idx[a1]^1], b2=key[idx[b1]^1];
				if (a2 == b1) continue; //同组钥匙
				if (a1==b1) g.Add(idx[b1]^1, idx[b1]); //同一把钥匙
				else
					g.Add(idx[a1], idx[b1]^1),g.Add(idx[b1], idx[a1]^1);
			}
			if (sat.Check(g,n))
				ans = mid,low=mid+1;
			else high=mid-1;
		}
		printf("%d\n",ans);
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: