2-Sat _ POJ 3678

看这个题的时候让我明白了前面博客中一个不明白的地方,一个关于2-Sat建边的问题,我们在2-Sat建边是,我们所建的边是要求选这个点,那么这个点之后的带你也是一定被选的,比如我们说A和B都是可以去0或1,他们满足A AND B = 1,那么A和B就必须都是1了,我们要怎么表示这个条件呢?我们要连接 A0 -> A1,B0 -> B1,这样我们如果选0的话,我们就必须要选1,那么他两个就一定不满足2-Sat的答案,所以只能选择1....

我们看一下这个题:

 A AND B = 1     添加条件:A0 -> A1 , B0 -> B1 

A AND B = 0 ,那么满足A为1时,B就一定要选0,相反B选1是,A一定选0 所以添加两个条件 : A1 -> B0 , B1 -> A0

A OR B = 1 , 它满足A = 0,B一定为 1,相反B= 0,A一定为1:A0 -> B1 , B0 -> A1

A OR B = 0 ,满足连个都是0,这个和第一个相似,我们要添加条件:A1 -> A0, B1 -> B0

A XOR B = 0,也就是两个必须是相同的,那么满足:A0 -> B0 , A1 -> B1 , B0 -> A0, B1 -> A1

A XOR B = 1,也就是两个元素都是不行同的,满足:A0 -> B1 , A1 -> B0 , B1 -> A0, B0 -> A1

那么程序就剩下2-Sat判断可行性问题了:

  

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <vector>
#include <algorithm>

using namespace std;
const int MAXN = 4000000;
const int MAXM = 2505;
int n,m;
char str[5];
struct Edge{
    int u,v,next;
}edge[MAXN];
int head[MAXM],k;
int dfn[MAXM],low[MAXM],stact[MAXM],vis[MAXM],index,num,tot,belong[MAXM];
void Init()
{
    k = 0;index = 0;num =0 ;tot = 0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(stact,0,sizeof(stact));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(belong));
}

void Add_edge(int u,int v)
{
    edge[k++].u = u;edge[k].v = v;edge[k].next = head[u];head[u] = k;
}
void Tarjan(int u)
{
    dfn[u] = low[u] = ++tot;
    stact[++index] = u;vis[u] = 1;
    for(int k = head[u];k != -1 ;k = edge[k].next)
    {
        int v = edge[k].v;
        if(!dfn[v]){
            Tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(vis[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u]){
        num ++;
        do{
            int v = stact[index];
            belong[v] = num;
            vis[v] = 0;
            index --;
        }while(u != stact[index+1]);
    }
}
int main()
{
    Init();
    scanf("%d%d",&n,&m);//i表示0,i+n表示1
    int a,b,c;
    for(int i =0 ;i < m;i ++)
    {
        scanf("%d%d%d%s",&a,&b,&c,str);
        if(str[0] == 'A')
        {
            if(c == 1){//必须都是1的情况我们可以让i -> i+n这样选择0的话就必须选择1,那么我们就可以推出矛盾
                Add_edge(a,a+n);Add_edge(b,b+n);
            }else{//其中一个为0,
                Add_edge(a+n,b);Add_edge(b+n,a);
            }
        }
        if(str[0] == 'O')
        {
            if(c == 0) {//两个都为0
                Add_edge(a+n,a);Add_edge(b+n,b);
            }
            else{//其中一个为 1
                Add_edge(a,b+n);Add_edge(b,a+n);
            }
        }
        if(str[0] == 'X'){
            if(c == 0){//a和b相同,同时为0或者同时为1
                Add_edge(a,b);Add_edge(b,a);Add_edge(a+n,b+n);Add_edge(b+n,a+n);
            }
            else{//a和b不同
                Add_edge(a,b+n);Add_edge(b,a+n);Add_edge(a+n,b);Add_edge(b+n,a);
            }
        }
    }
    for(int i = 0;i < 2*n;i ++)
        if(!dfn[i])
            Tarjan(i);
    for(int i = 0;i < n;i ++)
    {
        if(belong[i] == belong[i+n])
        {
            printf("NO\n");
            return 0;
        }
    }
    printf("YES\n");
}

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值