您的位置:首页 > 其它

POJ 2723 Get Luffy Out (2-SAT)

2017-07-20 11:05 441 查看

题意:

给你n对钥匙,一共2*n个钥匙。

然后不一定谁和谁配对,但给你这n个配对关系。

一对钥匙里,你只能选一把钥匙。

然后你面前有1扇门,门有两个锁,你只要任意打开一个锁就能进去,然后你面前又有一扇门。。。

给你n扇门各自所需要的锁。现在问你最多你可以过多少扇门。

注意配对关系i,j,只能选其中一个,所以这么建边:

ae(i,j+n) ae(j,i+n) ae(i+n,j) ae(j+n,i)

注意这里我是用i代表选,i+n代表不选。

但是这题要是这么建边无疑是麻烦了一点。。直接用一个数组key将钥匙的对应关系存下来,然后自然就不能同时选了,后边只需要讨论门就好了。

接下来是门:对于当前这个门,可以由i或j钥匙打开,

所以我们可以这么建边:

ae(key[i],j) ae(key[j],i);

代表着选i的对立点(不选i)就得选j,否则打不开门

代表着选j的对立点(不选j)就得选i,否则打不开门

然后跑tarjan判断能否可行。

我在做这题的时候看到别人有的是用二分求的最大门数,然而我觉得二分的话建边有些麻烦就没用(而且建边也会浪费不少操作。。),所以我就加两条边判断一次,不行的话就减减输出。(我一开始忘了在全开门之后减减了。。wa死我了。。2s时间只跑了450ms,所以根本没必要二分啊。。)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#include <queue>
#include <cmath>
#define pi acos(-1.0)
#define eps 1e-6
typedef long long int lli;
using namespace std;
const int maxn = 4200;

struct edge{
int from,to,v,next;
}ed[400000],door[20000];
int head[maxn];
int cnte;
void ae(int x,int y){
ed[++cnte].to = y;
ed[cnte].next = head[x];
head[x]=cnte;
}

int dfn[maxn],low[maxn],vis[maxn],stak[maxn],belong[maxn],cntc,cnts,index;//strong connected component //cnt of stack
void dfs(int u){
dfn[u]=low[u] = ++index;
stak[cnts++]=u;
vis[u]=1;
for(int i = head[u];i!=-1;i=ed[i].next){
int v = ed[i].to;
if(!dfn[v]){
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
cntc++;int v;
do{
v = stak[--cnts];
vis[v] = 0;
belong[v] = cntc;
}while(v!=u);
}
}
int n,m;
void tarjan(){
for(int i = 0;i < 2*n;i++){
if(!dfn[i]){
dfs(i);
}
}
}
int key[4220];
void ini(){
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(belong,0,sizeof(belong));
cntc=index=0;
}
bool jud(){
for(int i = 0;i < 2*n;i++){
if(belong[i]!=0 &&belong[i] == belong[key[i]]){
return false;
}
}
return true;
}
int main(){
int t;
while(~scanf("%d%d",&n,&m),n||m){
memset(head,-1,sizeof(head));memset(key,0,sizeof(key));
cnte = 0;
int a,b;
for(int i = 1;i <= n;i++){
scanf("%d%d",&a,&b);
key[a] = b;key[b] = a;
}
for(int i = 1;i <= m;i++){
scanf("%d%d",&door[i].from,&door[i].to);
}
int i;
for(i = 1;i <= m;i++){
ae(key[door[i].from],door[i].to);
ae(key[door[i].to],door[i].from);
ini();tarjan();
if(!jud()){
i--;
break;
}
}
if(i == m+1) i--;
printf("%d\n",i);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: