poj 2723 二分+2-sat判定

题意:给出n对钥匙,每对钥匙只能选其中一个,在给出每层门需要的两个钥匙,只要一个钥匙就能开门,问最多能到哪层。

 

思路:了解了2-SAT判定的问题之后主要就是建图的问题了,这里建图就是对于2*n个钥匙,分别分成a和a'两组,即选了比如a,b一组钥匙,选择了a则必须选择b',那么进行连边,而对于每层门,若该门能开,且选择了a',则b肯定需要选,那么a'和b连边。最后就是判定了,同样的 若a与a'在同一个连通分量中,表示无解。这题需要利用二分枚举答案m。

 

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#define MAXN 5005
#define MAXM 50005
#define inf 100000000
using namespace std;
struct Edge
{
    int v,next;
}edge[MAXM];
int n,m,tot;
int top,scnt,index;
int low[MAXN],dfn[MAXN],instack[MAXN],head[MAXN];
int stack[MAXN],fa[MAXN],a[MAXN],b[MAXN],hash[MAXN];
int x[MAXN],y[MAXN];
void init()
{
    top=scnt=index=tot=0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
}
void addedge(int u,int v)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void tarjan(int u)
{
    low[u]=dfn[u]=++index;
    instack[u]=1;
    stack[++top]=u;
    int v;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        v=edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        scnt++;
        do
        {
            v=stack[top--];
            instack[v]=0;
            fa[v]=scnt;
        }while(v!=u);
    }
}
bool check(int n)
{
    for(int i=1;i<=n;i++)
    {
        if(fa[i]==fa[i+n]) return false;
    }
    return true;
}
void build(int bound)
{
    init();
    for(int i=1;i<=n;i++)
    {
        addedge(a[i],b[i]+2*n);
        addedge(b[i],a[i]+2*n);
    }
    for(int i=1;i<=bound;i++)
    {
        addedge(x[i]+2*n,y[i]);
        addedge(y[i]+2*n,x[i]);
    }
    for(int i=1;i<=2*n;i++)
    {
        if(!dfn[i])tarjan(i);
    }
}
void solve()
{
    int ans=0;
    int low=0,high=m;
    while(low<=high)
    {
        int mid=(low+high)>>1;
        build(mid);
        if(check(2*n))ans=max(ans,mid),low=mid+1;
        else high=mid-1;
    }
    printf("%d\n",ans);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
		if(n==0&&m==0)break;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&a[i],&b[i]);
			a[i]++;b[i]++;
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&x[i],&y[i]);
			x[i]++;y[i]++;
		}
		solve();
    }
    return 0;
}


 

posted @ 2013-08-25 20:24  amourjun  阅读(166)  评论(0编辑  收藏  举报