POJ 3678 深刻理解2SAT

感觉:这题看了一晚上了,看了所有的解题报告才明天怎么回事……昨天自学没理解好2SAT啊,所以连边不知道怎么连,而且看了别人的解题报告了也看不明白为什么要那样连,晕……

思路:因为给出结点 a ,b,值 c,还有判断方式OP,这种一看当然就知道是用2SAT做了。为什么说是深刻理解2SAT呢,因为……2SAT中说过,只有关系确定的才能连边,否则不能连边;还有一个重要的是,如果某个条件必须为某个值时,自身与自身的相反条件也要连边,具体看下面解释:

现在设 2*a为1,2*a+1为0;当然 2*b为1,2*b+1为0:

1.当OP为’And‘时:

(1)当c=1时,那么只有a 与 b同时为1时,a  AND b才等于1,并且有且只有当a与b都为1时这个条件才成立,所以a与b一定要等1,所以连边<2*a+1,2*a>,<2*b+1,2*b>,表示不管怎么样,a与b的情况都等于1,即:当a等于0时a必等于1,b等于0时b必等于1,这个刚开始我看别人的解题报告就是这么说的,然后自己也没太理解,其实真正的内涵就是强制执行a与b都等于1 !(如果a等于1了的话当然这条边就没用了,如果a等于0的话,那么这条连就可以起到把a强制等于1以符合题目条件情况了,就是如此简单,得慢慢理解)

(2)当c=0时,那么当a等于0时,b可能为0也可以为1,所以是不确定关系,由上面说的一定是确定关系才能连边,所以a为0的情况就不能连边了;当a等于1时,b一定为0才能使 a AND b =0,所以连边:<2*a,2*b+1>,当然还有<2*b,2*a,+1>。

2.当OP为OR时,

(1)当c=1时,那么当a=1时,b=1或者b=0,所以当a=1时出现了两种关系,就是不确定了,就不用连边了;当a=0时,那么b一定=1,所以是确定关系,连边:<2*a+1,2*b>,当然还有<2*b+1,2*a>。

(2)当c=0时,那么只有当a=b=0这个关系,所以这个和上面1(1)情况就一样了,上面是强制执行a=b=1的情况,而这里因为只有a=b=0的情况,所以也要强制执行a=b=0,即连边:<2*a,2*a+1>,<2*b,2*b+1>。

3.当OP为XOR时,因为如果a=1,那么b必=0;a=0,b必=1;b=1,a必=0;b=0,a必=1。如此看,这四个关系都是确定的,所以都要连边,但是其实我们可以不连,一条边都不用连,因为出a=1的时候一定不会再出现a=0了,这四条边是不会产生矛盾的,所以强连通缩点后不会出现belong[2*a]=belong[2*a+1]的情况的,所以连了也没用,只是多加了点判断的时间罢了……这在别人的解题报告里说的是形成了组环了,都是一个意思。比如:a=1,b=0与b=0,a=1在tarjan中会形成一个新的结点,也就是自环,所以……在异或这种情况中只能选择a=0或者a=1,所以不会出现矛盾……故不用连边了!

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <list>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#define PI acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define sca(a) scanf("%d",&a)
#define sc(a,b) scanf("%d%d",&a,&b)
#define pri(a) printf("%d\n",a)
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define MM 200005
#define MN 2010
#define INF 1000000007
#define eps 1e-7
using namespace std;
typedef long long ll;
int DFN[MN],vis[MN],LOW[MN],Stack[MN*10],belong[MN],tem,Count,top;
vector<int>e[MN];
void tarjan(int u)
{
    DFN[u]=LOW[u]=++tem;
    vis[u]=true;
    Stack[++top]=u;
    int v,i,l=e[u].size();
    for(i=0;i<l;i++)
    {
        v=e[u][i];
        if(!DFN[v])
        {
            tarjan(v);
            LOW[u]=min(LOW[u],LOW[v]);
        }
        else if(vis[v]&&DFN[v]<LOW[u]) LOW[u]=DFN[v];
    }
    if(DFN[u]==LOW[u])
    {
        Count++;
        do
        {
            v=Stack[top--];
            vis[v]=false;
            belong[v]=Count;
        }while(v!=u);
    }
}
bool twoSAT(int n)
{
    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()
{
    int n,m,i,a,b,c;
    char s[5];
    sc(n,m);
    for(i=0;i<m;i++)
    {
        scanf("%d%d%d%s",&a,&b,&c,s);
        if(s[0]=='A')
        {
            if(c)
            {
                e[2*a+1].push_back(2*a);
                e[2*b+1].push_back(2*b);
            }
            else
            {
                e[2*a].push_back(2*b+1);
                e[2*b].push_back(2*a+1);
            }
        }
        else if(s[0]=='O')
        {
            if(c)
            {
                e[2*a+1].push_back(2*b);
                e[2*b+1].push_back(2*a);
            }
            else
            {
                e[2*a].push_back(2*a+1);
                e[2*b].push_back(2*b+1);
            }
        }
    }
    if(twoSAT(n)) puts("YES");
    else puts("NO");
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值