HDU 1816 Get Luffy Out *(2-SAT)

HDU 1816 Get Luffy Out *(2-SAT)

http://acm.hdu.edu.cn/showproblem.php?pid=1816

题意:

你有2N把钥匙,你的前面按顺序有M个门,每个门有两个锁,现在你必须用这2N把钥匙去开门. 一个门只要一把锁被打开了这个门就可以被打开.且2N把钥匙还有N个配对关系,每一对的两把钥匙i与j,如果用了i钥匙,那么j钥匙永远不能再用.如果用了j钥匙,那么i钥匙永远不能在用.(这里一把钥匙i可能属于多个配对关系,而POJ2723中2N把钥匙是正好被分成了N对,每把钥匙只出现1次)

分析: 本题类似POJ2723:

http://blog.csdn.net/u013480600/article/details/34891151

       由于POJ我没用二分做,所以这里我用二分再做一次.

       对于2-SAT的图,有两类关系我们需要添加边.

第一类,对于a与b钥匙配对,那么a与b不能同时出现,有:

add(a,0,b,1)  add(b,0,a,1) 这里a=0 表示用a钥匙.

第二类,对于门上有锁a与b,那么有:

add(a,1,b,0)  add(b,1,a,0) a=1 表示不用钥匙a

其实该题直接用POJ2723的代码就能AC,刘汝佳的模板不用修改,可能用tarjan算法需要修改些东西吧?

AC代码: 二分125ms,比直接遍历速度(406ms)要快不少

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=2000+50;
int n,m;
int a[maxn],b[maxn];//每对钥匙
int c[maxn],d[maxn];//每个门上的锁对
struct TwoSAT
{
    int n;
    vector<int> G[maxn*2];
    int S[maxn*2],c;
    bool mark[maxn*2];

    bool dfs(int x)
    {
        if(mark[x^1]) return false;
        if(mark[x]) return true;
        mark[x]=true;
        S[c++]=x;

        for(int i=0;i<G[x].size();i++)
            if(!dfs(G[x][i])) return false;
        return true;
    }

    void init(int n)
    {
        this->n=n;
        for(int i=0;i<2*n;i++) G[i].clear();
        memset(mark,0,sizeof(mark));
    }

    void add_clause(int x,int xval,int y,int yval)
    {
        x=x*2+xval;
        y=y*2+yval;
        G[x].push_back(y);
    }

    bool solve()
    {
        for(int i=0;i<2*n;i+=2)
        if(!mark[i] && !mark[i+1])
        {
            c=0;
            if(!dfs(i))
            {
                while(c>0) mark[S[--c]]=false;
                if(!dfs(i+1)) return false;
            }
        }
        return true;
    }
}TS;

bool ok(int mid)
{
    TS.init(n);
    for(int i=0;i<n/2;i++)
    {
        TS.add_clause(a[i],0,b[i],1);
        TS.add_clause(b[i],0,a[i],1);
    }
    for(int i=0;i<mid;i++)  //注意这里是i<mid,而不是i<m
    {
        TS.add_clause(c[i],1,d[i],0);
        TS.add_clause(d[i],1,c[i],0);
    }
    return TS.solve();
}
int main()
{
    while(scanf("%d%d",&n,&m)==2&&n)
    {
        n*=2;

        for(int i=0;i<n/2;i++)  scanf("%d%d",&a[i],&b[i]);
        for(int i=0;i<m;i++)    scanf("%d%d",&c[i],&d[i]);

        int L=0, R=m;
        while(R>L)
        {
            int mid = L+(R-L+1)/2;
            if(ok(mid)) L=mid;
            else R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}


你可能感兴趣的:(ACM)