【模板】2-SAT 问题
发布日期:2021-06-30 10:31:51 浏览次数:2 分类:技术文章

本文共 2643 字,大约阅读时间需要 8 分钟。

问题

n n n个布尔变量, m m m组限制,每组限制形如 a , x , b , y a,x,b,y a,x,b,y

表示 a = x a=x a=x b = y b=y b=y一定有一个成立(其中 x , y x,y x,y是布尔值)

求出一组可行解,或者说明这是不可能的


拆点,原图中的点 i i i表示 f a l s e false false, i + n i+n i+n表示 t r u e true true

对于 a = t r u e ∣ ∣ b = = f a l s e a=true||b==false a=trueb==false一定成立其中的至少一个

如果 a a a的值为 f a l s e false false那么 b b b一定是 f a l s e false false

对应的,我们连边 a − > b a->b a>b

如果 b b b的值为 t r u e true true那么 a a a一定是 t r u e true true

对应的,我们连边 b + n − > a + n b+n->a+n b+n>a+n

这样有什么好处…看个图

在这里插入图片描述

比如这里 x 1 x_1 x1代表的 t r u e true true f a l s e false false居然在一个强连通里面,结合边的意义就是说

x 1 x_1 x1 t r u e true true, 可以推断 x 1 x_1 x1 f a l s e false false,所以矛盾,即 x 1 x_1 x1不是 t r u e true true

x 1 x_1 x1 f a l s e false false, 可以推断 x 1 x_1 x1 t r u e true true,所以矛盾,即 x 1 x_1 x1不是 f a l s e false false

所以 x 1 x_1 x1啥也不是,无解。

所以判断有没有解,只需要看一下同一个点的两个状态是否在同一个强连通中即可

在这里插入图片描述

在这副图中,如何构造解??

显然如果取 x 1 = f a l s e x_1=false x1=false可以推断 x 1 = t r u e x_1=true x1=true,所以我们应该选择 x 1 = t r u e x_1=true x1=true

也就是两个状态中,被指向的那个状态

其实就是 D A G DAG DAG中的拓扑序,而 t a r j a n tarjan tarjan算法的 s c c scc scc是由栈形成的

一定是经过了前驱的强连通分量到达被指向的强连通分量,然后弹栈

所以 s c c scc scc编号越小的,是被指向的强连通,我们应该选择

#include 
using namespace std;const int maxn = 4e6+10;struct edge{
int to,nxt;}d[maxn]; int head[maxn],cnt=1;void add(int u,int v){
d[++cnt] = ( edge ){
v,head[u]},head[u] = cnt;}int n,m;int low[maxn],dfn[maxn],scc[maxn],vis[maxn],stac[maxn],sc,id,top;void tarjan(int u){
dfn[u] = low[u] = ++id; stac[++top] = u; vis[u] = 1; for(int i=head[u];i;i=d[i].nxt ) {
int v = d[i].to; 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] ) {
int temp; ++sc; while( temp=stac[top--] ) {
vis[temp] = 0, scc[temp] = sc; if( temp==u ) break; } }}int main(){
cin >> n >> m; for(int i=1;i<=m;i++) {
int a,x,b,y; scanf("%d%d%d%d",&a,&x,&b,&y); if( x==0 && y ==0 ) add( a+n,b),add(b+n,a);//一方为真,另一方一定是假 else if( x==0&&y==1 ) add( a+n,b+n ),add( b,a ); else if( x==1&&y==0 ) add( a,b ),add( b+n,a+n ); else if( x==1 && y==1 ) add( a,b+n ),add( b,a+n ); } for(int i=1;i<=n+n;i++) if( !dfn[i] ) tarjan(i); for(int i=1;i<=n;i++) if( scc[i]==scc[i+n] ) {
printf("IMPOSSIBLE\n"); return 0; } puts("POSSIBLE"); for(int i=1;i<=n;i++) {
if( scc[i]>scc[i+n] ) printf("1 "); else printf("0 "); } return 0;}

转载地址:https://issue-is-vegetable.blog.csdn.net/article/details/115384396 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:P4322 [JSOI2016]最佳团体(树上背包+01分数规划)
下一篇:P3159 [CQOI2012]交换棋子(模型转换:拆点费用流)

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月26日 05时20分42秒