POJ 2723 Get Luffy Out

题意:给你2n把钥匙,分成n组,每组只能选一把,然后另一把就不能用了。同时,有m个门,每个门上有两把锁,打开一个就可以通过,问最多可以通过几扇门;

思路:2-sat,假如(a,b)是同一组的钥匙,那么就有 a--b',b--a';

(c,d)是同一门上的锁那么就有d'--c,c'--d;(为什么不是c--d',d--c'?  因为2-SAT其实就是找不相容的点对,你开了c,当然也可以开d,并不能得到d',所以c,d是相容的,但是不开d,就只能开c)


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;
const int N = 2000*4;
vector<int> vec[N];
int n, m, id, cnt;
int dfn[N], vis[N], low[N], belong[N],key1[N],key2[N],door1[N],door2[N];
stack<int> s;
void init(){
	memset(vis,0,sizeof(vis));
	memset(dfn,-1,sizeof(dfn));
	memset(low,-1,sizeof(low));
	memset(belong,-1,sizeof(belong));
	id = cnt = 0;
	while(!s.empty())s.pop();
	for(int i = 0; i < 4*n; i++)vec[i].clear();
}
void tarjan(int u){
	dfn[u] = low[u] = id++;
	vis[u] = 1;
	int sz = vec[u].size();
	s.push(u);
	for(int i = 0; i < sz; i++){
		int v = vec[u][i];
		if(dfn[v] == -1){
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if(vis[v] == 1){
			low[u] = min(low[u], dfn[v]);
		}
	}
	if(low[u] == dfn[u]){
		cnt++;
		while(!s.empty()){
			int temp = s.top();
			s.pop();
			vis[temp]=0;
            belong[temp]=cnt;
			if(temp == u)break;
		}
	}
}
bool judge(int x){
	init();
	for(int i = 0; i < n; i++){
		vec[key1[i]*2].push_back(key2[i]*2+1);
		vec[key2[i]*2].push_back(key1[i]*2+1);
	}
	for(int i = 0; i < x; i++){
		vec[door1[i]*2+1].push_back(door2[i]*2);
		vec[door2[i]*2+1].push_back(door1[i]*2);
	}
	for(int i = 0; i < 4*n; i++){
		if(dfn[i] == -1)tarjan(i);
	}
	for(int i = 0; i < 2*n; i++){
		if(belong[i*2] == belong[i*2+1])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,r = m,ret;
		while(l <= r){
			int mid = (l+r)>>1;
			if(judge(mid))ret = mid,l = mid+1;
			else r = mid -1;
		}
		printf("%d\n",ret);
	}
	return 0;
}


你可能感兴趣的:(poj,2-sat)