题意
假定有
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
1≤n,m≤10000
1
≤
T
≤
10
1 \leq T \leq 10
1≤T≤10
思路
考虑2-Sat问题,给定二元组
(
a
,
b
)
(a,b)
(a,b),如果这个二元组是满足的,那么至少有一个为真值。这就可以写出一个逻辑表达式:
a
∨
b
a \lor b
a∨b。
对于现在这个问题,逻辑表达式就是:
a
∨
b
,
b
∨
c
,
c
∨
a
a \lor b, b \lor c, c \lor a
a∨b,b∨c,c∨a,这一点可以通过真值表证明。
然后再跑一遍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上测过,因此输出了一下构造方案,检查是否正确。