2-sat系列(二) POJ 3678 Katu Puzzle

本文介绍了2-SAT问题,并通过POJ 3678 Katu Puzzle题目进行讲解。文章详细阐述了如何根据布尔变量的AND、OR、XOR约束条件构建图,并分析了错误的构图方法。重点讨论了AND和OR条件下的边构造,强调了为何a-->a'表示a必须为1,b-->b'表示b必须为1的原因。最后,说明了通过判断强连通分量来确定问题是否有解的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目大意】

n个布尔变量Xi,现在有m个约束条件,给出a op b = c,给出每个a b c 和 op。其中op可能是AND,OR,XOR。要求这n个布尔变量是否存在一组可能的取值,使得满足所有的约束条件。

【思路分析】

这题的构图的思路并不难,因为每个点就只有2种状态0或者1,所以构图就显得容易!我们设a为0,a'为1,b为0,b‘为1

那么根据3种操作符和c的值就可以构建下面这些边

AND    c==1     a-->a' b-->b'这2条边表示a,b必须同时取1条件才成立

           c==0    a'-->b  b'-->a   第一条表示在a取1的情况下b必须取0,才能使a AND b为0,第2条边类推

OR      c==1   a-->b'   b-->a'  第一条边表示a取0的情况下b必须取1才能使结果为1,第2条边类推

           c==0   a'-->a b'-->b  这2条边表示只有当a,b同时为0时条件才成立

XOR    c==1  a-->b'  a'-->b  b-->a'  b'-->a   表示只有a,b不同是条件才为1

          c==0   a-->b   a'-->b'  b-->a  b'-->a'   表示只有a,b相同时条件才为0

最后判断下相对2点是否在同一强连通分量重即可

【疑难解答】

(1)这题其他的构边都也许很好理解,就是AND c==1 a-->a' b-->b'   OR   c==0 a'-->a b'-->b  这2条边不能理解,为什么要这么构边?

嘿嘿,在说之前,先看看我原先错误的构边,我原来对于AND c==1这种情况是这样构边的 a'-->b' b'-->a'表示a为1的情况下b也为1,b为1的情况下a也为1条件才成立,表面上看没错,其实这是错误的构边方法,我的这种构边隐含的意思说a,b还可以有其他情况,a可以为0,b也可以为0,只不过在a为1 的情况下b必须为1,在b为1 的情况下a必须为1!其实不然,只要a为0或者b为0条件就不成立了,所以a,b必须同时为1,a和b的值是被固定死了的,a必须是1,b必须也是1,这个条件才成立,其他条件都不成立!所以构建 a-->a'表示a必须为1,b-->b' 表示b必须为1,条件才成立。我们可以从不相容点对看这种情况,在这种情况下不相容点对有3对 (a=0,b=0),(a=0,b=1)和(a=1,b=0)而在这3个不相容点对中构造的边都为同一条边 即a,b同时为1条件才成立,其他都不成立,也验证了上面这一点!

(2)为什么 a-->a'表示a必须为1,b-->b' 表示b必须为1

a'-->a其实就是表示0-->1选择了0必须选择1,我们从判断2-sat有无解的角度来看,当a 和a'在同一个强连通分量的时候就代表无解了,那么如果有个a'在某个强连通分量里,就代表该强连通分量选择了a'(1),那如果a也在这个强连通分量里的话就代表选择了a(0),对于其他条件来说a为0或者1都没有关系,这2者都可以选其一,而对于AND c==1 a-->a' b-->b' OR c==0 a'-->a b'-->b这2种情况来说是不行的,a,b的值必须固定!而我们增加了a'-->a这条边则可以产生了这一更严格要求,使a,b提前绑定!其实就是不能有条a'出去的路径回到a来,不能从1出去的路径回到0来,选择0(住:有条边指向0,就代表选择了0)!

 

源代码:

# include<cstdio>
# include<cstring>
# include<stack>
# include<vector>
using namespace std;
# define N 2005
int dfn[N],low[N],instack[N],belong[N],n,m,now,cnt;
vector<int> vec[N];
stack<int> sta;

inline int min(int a,int b)
{
    return a<b?a:b;
}

void tarjan(int v)
{
    int i,u;
    dfn[v]=low[v]=++now;
    sta.push(v);
    instack[v]=1;
    for(i=0;i<vec[v].size();i++)
    {
        u=vec[v][i];
        if(!dfn[u])
        {
            tarjan(u);
            low[v]=min(low[v],low[u]);
        }
        else if(instack[u])
            low[v]=min(low[v],dfn[u]);
    }
    if(dfn[v]==low[v])
    {
        cnt++;
        while(!sta.empty())
        {
            u=sta.top();
            sta.pop();
            instack[u]=0;
            belong[u]=cnt;
            if(u==v)
                break;
        }
    }
}


int main()
{
    int i,a,b,c,flag;
    char s[5];
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        flag=now=cnt=0;
        memset(instack,0,sizeof(instack));
        memset(belong,0,sizeof(belong));
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d%s",&a,&b,&c,s);
            if(s[0]=='A')
            {
                if(c==1)
                {
                    vec[2*a].push_back(2*a+1);
                    vec[2*b].push_back(2*b+1);
                }
                else if(c==0)
                {
                    vec[2*a+1].push_back(2*b);
                    vec[2*b+1].push_back(2*a);
                }
            }
            else if(s[0]=='O')
            {
                if(c==1)
                {
                    vec[2*a].push_back(2*b+1);
                    vec[2*b].push_back(2*a+1);
                }
                else if(c==0)
                {
                    vec[2*a+1].push_back(2*a);
                    vec[2*b+1].push_back(2*b);
                }
            }
            else
            {
                if(c==1)
                {
                    vec[2*a].push_back(2*b+1);
                    vec[2*a+1].push_back(2*b);
                    vec[2*b].push_back(2*a+1);
                    vec[2*b+1].push_back(2*a);
                }
                else
                {
                    vec[2*a].push_back(2*b);
                    vec[2*b].push_back(2*a);
                    vec[2*a+1].push_back(2*b+1);
                    vec[2*b+1].push_back(2*a+1);
                }
            }
        }
        for(i=0;i<2*n;i++)
            if(!dfn[i])
                tarjan(i);
        for(i=0;i<2*n;i+=2)
            if(belong[i]==belong[i+1])
            {
                flag=1;
                break;
            }
        if(flag)
            printf("NO\n");
        else
            printf("YES\n");
        for(i=0;i<2*n;i++)
            vec[i].clear();
        while(!sta.empty())
            sta.pop();
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值