POJ 2723 && HDU 1816 Get Luffy Out(2-SAT+二分)

Description
有m层楼,从一层到m层,要进入每层都要打开位于该层的两道门中的至少一道。门锁有2n种,每个门锁为2n种中的一种,可以重复。有2n把钥匙,分别对应2n种锁,但是钥匙两两一组,共n组,每组只能选一个来开门,被选中的可以多次使用,另一个一次都不能用。问最多能上多少层
Input
第一行两个整数n和m分别表示钥匙组数和楼层数,之后n行每行两个整数表示一组钥匙能开的锁的编号,最后m行每行两个整数表示每层两个门锁编号
Output
输出最多能上多少层
Sample Input
3 6
0 3
1 2
4 5
0 1
0 2
4 1
4 2
3 5
2 2
0 0
Sample Output
4
Solution
首先二分层数,层数确定则要开的门也确定,那么就转化成为一个2-SAT问题,首先对于每组钥匙a,b,有a->~b,b->~a,然后对于每层的门,设锁为c,d的话,由于最少要开一个门,故有~c->d,~d->c,将关系图建好之后用tarjan算法求强联通分量并判断是否有使得布尔公式值为真的一组布尔变量复制即可
Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define maxn 1111*4
vector<int>g[maxn];  
stack<int>st;  
int n,m,scc,index;  
int low[maxn],dfn[maxn],instack[maxn],fa[maxn];  
int key1[maxn],key2[maxn];
int door1[maxn],door2[maxn];
void init()//初始化 
{  
    scc=index=0;  
    while(!st.empty())st.pop();  
    for(int i=0;i<maxn;i++)g[i].clear();  
    memset(dfn,0,sizeof(dfn));  
    memset(low,0,sizeof(low)); 
    memset(instack,0,sizeof(instack));  
}  
void tarjan(int u)//求强联通分量 
{  
    dfn[u]=low[u]=++index;  
    instack[u]=1;  
    st.push(u);  
    int v,size=g[u].size();  
    for(int i=0;i<size;i++)  
    {  
        v=g[u][i];  
        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])  
    {  
        scc++;  
        do  
        {  
            v=st.top();  
            st.pop();  
            fa[v]=scc;  
            instack[v]=0;  
        }while(v!=u);  
    }  
}  
void build(double mid)  
{  
    for(int i=1;i<=n;i++)//对所有的钥匙关系建边 
    {
        g[key1[i]*2].push_back(key2[i]*2+1);
        g[key2[i]*2].push_back(key1[i]*2+1);
    }
    for(int i=1;i<=mid;i++)//对要开的门的关系建边 
    {
        g[door1[i]*2+1].push_back(door2[i]*2);
        g[door2[i]*2+1].push_back(door1[i]*2);
    }
}  
bool check()//判断是否存在使得布尔公式值为真的一组布尔变量赋值 
{  
    for(int i=0;i<2*n*2;i++)//求强联通分量 
        if(!dfn[i]) 
            tarjan(i);  
    for(int i=0;i<2*n;i++)  
        if(fa[2*i]==fa[2*i+1])//矛盾 
            return false;  
    return true;  
}  
int main()
{
    while(scanf("%d%d",&n,&m),n||m)
    {
        for(int i=1;i<=n;i++)
            scanf("%d%d",&key1[i],&key2[i]);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&door1[i],&door2[i]);
        int left=0,right=m,ans=0;
        while(left<=right)//二分层数,用ans记录答案 
        {
            int mid=(left+right)/2;
            init();
            build(mid);
            if(check())
            {
                ans=max(ans,mid);
                left=mid+1;
            }
            else
                right=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(POJ 2723 && HDU 1816 Get Luffy Out(2-SAT+二分))