Majority 3-SAT(2-Sat)

题意

假定有 n n n个还未被赋值的布尔变量 x 1 , x 2 , … , x n x_1,x_2,\dots ,x_n x1,x2,,xn。对于三元组 ( a , b , c ) (a,b,c) (a,b,c),如果是满足的,那么 a , b , c a,b,c a,b,c至少有两个是取真值的,这里的 a , b , c a,b,c a,b,c是布尔变量或者是布尔变量取反。
现在给定 m m m个三元组,问能否给 n n n个布尔变量赋值,使得这 m m m个三元组都是满足的。

数据范围

1 ≤ n , m ≤ 10000 1 \leq n,m \leq 10000 1n,m10000
1 ≤ T ≤ 10 1 \leq T \leq 10 1T10

思路

考虑2-Sat问题,给定二元组 ( a , b ) (a,b) (a,b),如果这个二元组是满足的,那么至少有一个为真值。这就可以写出一个逻辑表达式: a ∨ b a \lor b ab
对于现在这个问题,逻辑表达式就是: a ∨ b , b ∨ c , c ∨ a a \lor b, b \lor c, c \lor a ab,bc,ca,这一点可以通过真值表证明。
然后再跑一遍2-Sat的模板即可。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <cmath>

using namespace std;

const int N = 20010, M = 60010;

int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], ts, id[N], cnt;
bool ins[N];
stack<int> stk;

void add(int a,int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void tarjan(int u)
{
    low[u] = dfn[u] = ++ ts;
    stk.push(u), ins[u] = true;
    for(int i=h[u];~i;i=ne[i]){
        int j = e[i];
        if(!dfn[j]){
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if(ins[j]) low[u] = min(low[u], dfn[j]);
    }
    if(dfn[u] == low[u]){
        int y;
        cnt ++;
        do{
            y = stk.top();
            stk.pop();
            id[y] = cnt;
            ins[y] = false;
        }while(y != u);
    }
}

void init()
{
    memset(h,-1,sizeof(h));
    memset(ins,false,sizeof(ins));
    while(!stk.empty()) stk.pop();
    memset(id,0,sizeof(id));
    idx = 0, cnt = 0, ts = 0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
}

void getg(int a,int b,int c)
{
    int ta = abs(a), tb = abs(b), tc = abs(c);
    ta --, tb --, tc --;
    int a0 = 2 * ta, a1 = a0 + 1;
    int b0 = 2 * tb, b1 = b0 + 1;
    int c0 = 2 * tc, c1 = c0 + 1;
    if(a < 0) swap(a0, a1);
    if(b < 0) swap(b0, b1);
    if(c < 0) swap(c0, c1);
    add(a0, b1), add(b0, a1);
    add(a0, c1), add(c0, a1);
    add(b0, c1), add(c0, b1);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d",&n,&m);
        while(m--){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            getg(a,b,c);
        }
        for(int i=0;i<2*n;i++){
            if(!dfn[i]){
                tarjan(i);
            }
        }
        bool flag = true;
        for(int i=0;i<n;i++){
            if(id[2*i] == id[2*i+1]){
                flag = false;
                break;
            }
        }
        if(flag){
            puts("yes");
            for(int i=0;i<n;i++){
                if(id[2*i] < id[2*i+1]) printf("0 ");
                else printf("1 ");
            }
            printf("\n");
        }
        else puts("no");
    }
    return 0;
}
/*
4
3 2
1 2 3
-1 -2 3
3 2
1 2 3
-1 -2 -3
6 5
1 2 3
-2 -3 -4
3 4 5
-4 -5 -6
5 6 1
1 2
1 -1 1
-1 1 -1
*/

备注

代码未在oj上测过,因此输出了一下构造方案,检查是否正确。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值