poj-2723(2-sat+二分)

题目链接:http://poj.org/problem?id=2723

题意:有m层楼,每楼有一扇门,门上有两把锁,打开其中一把就可以打开门,门只能顺序打开

现在有n对不同的钥匙(即2*n把钥匙),每对钥匙只能有一把,问最多打开几扇门

 

解题思路:

因为钥匙只能二选一,所以想到2-sat

因为每个门的锁是限制的,我们对其钥匙建图,锁来限制。

 

怎么建图呢?

假如锁为 a,b,我们选a或者b是没有限制条件的都可以打开,如果选  a'必须选b所以建边(a',b) ,如果选b‘必须选a,所以建(b',a);

当a,b相等时,即都是a,选a即可,不能选a',这个一定要表示出来,我们建(a',a),使选a'变成不能成立。因为连了边使选了a',一定要选a。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=4100;
int n,m;
struct node{
    int u,v,next;
}e[maxn];
int key[maxn],h[maxn],low[maxn],dfn[maxn];
int visit[maxn],st[maxn],belong[maxn];
int cnt,tot,top,num,a[maxn],b[maxn];

void init()
{
    memset(h,-1,sizeof(h));
    memset(visit,0,sizeof(visit));
    memset(dfn,0,sizeof(dfn));
    memset(belong,0,sizeof(belong));
    cnt=num=top=tot=0;    
}

void add(int u,int v)//连边 
{
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].next=h[u];
    h[u]=cnt++;
}

void tarjan(int u)
{
    low[u]=dfn[u]=++tot;
    visit[u]=1;
    st[++top]=u;
    for(int i=h[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        if(visit[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int t;
        num++;
        do{
            t=st[top--];
            visit[t]=0;
            belong[t]=num;
        }while(t!=u);
    }
}

bool jug(int x)
{
    init();//每次初始化 
    for(int i=0;i<x;i++)//建边 
    {
        if(a[i]==b[i])
            add(key[a[i]]^1,key[a[i]]);
        else
        {
            add(key[a[i]]^1,key[b[i]]);
            add(key[b[i]]^1,key[a[i]]);
        }
    }
    for(int i=0;i<2*n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=0;i<n;i++)
        if(belong[2*i]==belong[2*i+1])//不成立 
            return false;
    return true;
}

int main()
{
    std::ios::sync_with_stdio(false);
    int x,y;
    while(cin>>n>>m)
    {
        if(!n&&!m)
            break;
        for(int i=0;i<n;i++)
        {
            cin>>x>>y;
            key[x]=2*i;
            key[y]=2*i+1;
        }
        for(int i=0;i<m;i++)
            cin>>a[i]>>b[i];    
        int l=0,r=m,ans=0;
        while(l<=r)//二分答案 
        {
            int mid=(l+r)/2;
            if(jug(mid))
            {
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        cout<<ans<<endl;
    }    
    return 0;
}

 

上一篇:二叉树的遍历


下一篇:python死磕三之类与对象