HDU 3715 Go Deeper(2-SAT)

HDU 3715 Go Deeper(2-SAT)

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

题意:

        有下面一个递归程序:

        go(int dep, int n, int m)

        begin

        output the value of dep.

        if dep < m and x[a[dep]] + x[b[dep]]!= c[dep] then go(dep + 1, n, m)

        end

        问你dep最多能达到什么值?由题意可知dep<=m,其中a与b与c数组都是m大小的,数组下标从0到m,然后x数组是下标从0到n-1的.且c数组的取值为0或1或2.a与b数组的取值是0到n-1.x数组的取值是0或1.

现在给出了a,b,c数组对应下标的所有值,但是还不知道x数组的取值,问你如果你来设定x的值,可以使dep最大达到多少?

分析:

        由于x数组只能去0或1,可以看出该题就是2-SAT问题. 我们只要2分dep的值即可的出解.

        假设当前dep=mid,然后对于下标从0到mid-1来说,有下面关系:

        x[a[0]]+x[b[0]] != c[0]

        x[a[1]]+x[b[1]] != c[1]

        

        x[a[mid-1]]+x[b[mid-1]] !=c[mid-1]

        然后我们合理的设置一下x数组的值,看看是否能满足这前mid个条件.由于a,b,c数组的值都已经知道了,比如  a[0]=1,b[0]=2,c[0]=2

        有x[1]+x[2] !=2 ,那么我们可以推出边: add_clause(1,1,2,0)且 add_clause(2,1,1,0).

       一般性结论有:

        x[a]+x[b]= 0 -> add(a,0,b,1)  add(b,0,a,1)

        x[a]+x[b]=1 ->add(a,1,b,1)  add(a,0,b,0) add(b,1,a,1)add(b,0,a,0)

        x[a]+x[b]=2 ->add(a,1,b,0)  add(b,1,a,0)

        二分dep,建图2-SAT,直接解决.

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn= 200 +10;
const int maxm= 10000+10;
int n,m;
int a[maxm],b[maxm],c[maxm];
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<mid;i++)
    {
        if(c[i]==0)
        {
            TS.add_clause(a[i],0,b[i],1);
            TS.add_clause(b[i],0,a[i],1);
        }
        else if(c[i]==1)
        {
            TS.add_clause(a[i],0,b[i],0);
            TS.add_clause(a[i],1,b[i],1);
            TS.add_clause(b[i],0,a[i],0);
            TS.add_clause(b[i],1,a[i],1);
        }
        else if(c[i]==2)
        {
            TS.add_clause(a[i],1,b[i],0);
            TS.add_clause(b[i],1,a[i],0);
        }
    }
    return TS.solve();
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
            scanf("%d%d%d",&a[i],&b[i],&c[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;
}


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值