2-Sat

Posted handsome-zlk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2-Sat相关的知识,希望对你有一定的参考价值。

先推两篇dalao资料:

https://wenku.baidu.com/view/afd6c436a32d7375a41780f2.html

https://blog.csdn.net/jarjingx/article/details/8521690

好像还有https://wenku.baidu.com/view/0f96c3daa58da0116c1749bc.html,不过我还没有看过

花了几天搞了搞这个东西

写一点小小的心得体会

由于本人是蒟蒻,而且这篇小笔记其实是留给自己看的,所以可能不会用什么高深的话去解释这个东西,也不会讲的非常详细,如果看完那两位大佬的博文还不是很懂,或许可以再看一看我的这篇笔记,希望能有所帮助

一、概念

至于2-Sat,它是解决一个类给了你一个图中一大坨关系,然后让你判断这些关系同时成立的图是否存在这样一类问题,为什么研究2-sat呢?无可置疑的是它很有用。为什么不研究3-sat乃至k更大的情况呢,因为它们已经被证明为是 NP完全问题了,在更多的情况下,2-sat不仅仅是元素个数最多的集合含有2个元素,而是每个集合都含有2个元素,且这2个元素不允许同时取出,这篇文章主要讨论的就是这样一种特殊的模型。这类题都是比较裸的,看一眼题就能知道要用2-Sat,所以可能并没有很多比赛考过这个东西。

二、算法流程&&说明

 

捋一遍2-sat方法流程

1.根据题意建图

连接若取a就必须取b这些路径

若条件是不能取a,你可以让a连到a’,矛盾即可

不懂为什么有人奇怪要建反向边,不是不建

2.Tarjan缩点

没什么好说的

缩起来的这一个点表示在这个环里面任取一个点就要取完这一个环,其实这是为了可以在图中进行拓扑排序和判断是否可行

3.判断是否存在

这个,嗯,如果有两个对应点在一个环里,就说明若取a必取a‘,矛盾

如果没有,就说明存在这样一个满足条件的图

4.拓扑排序

Tarjan缩点是个反拓扑排序,不必再排

5.根据拓扑排序输出方案

这个东西想了很久,发现自己过于zz,其实利用对称性即可证明这个东西,当a的拓扑序大于a‘时,只有这两种情况:

1)两者有路径相连,即若取a‘则必取a,故不能取a‘,只可取a

2)两者无路径相连,对称性这个东西其实那两位大佬都讲的很清楚,不再赘述,就直接引用一下两个结论:

由于点对称,边对称,则图对称,所以设a点有x个前序节点,有y个后序节点,则a’点有y个前序节点,有x个后序节点,而且互相对应

明确了这一点,其实就不难证明了,只需要将和a有关的所有节点通通取走,对应的通通不取,其实对其他的节点没有任何干扰,其他节点也只会出现这两种情况,所以可以简要证明拓扑排序的正确性

 

三、模板

 

洛谷P4782 【模板】2-SAT 问题

题目背景

2-SAT 问题 模板

输入格式:

 第一行两个整数n和m,意义如体面所述。

接下来m行每行4个整数 i a j b,表示“xi?为a或xj?为b”(a,b∈{0,1})

 

输出格式:

 如无解,输出“IMPOSSIBLE”(不带引号); 否则输出"POSSIBLE"(不带引号),下 一行n个整数x1?~xn?xi?∈{0,1}),表示构造出的解。

 

输入输出样例

输入样例#1: 
3 1
1 1 3 0
输出样例#1: 
POSSIBLE
0 0 0

若取-a则必须取b
若取-b则必须取a
剩下的套个板子即可

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(long long i=a;i<=b;i++)
#define MAXN 2000500
using namespace std;
typedef long long ll;
ll n,m,fir[MAXN],to[MAXN],nxt[MAXN],tot=-1;
ll dfn[MAXN],col[MAXN],low[MAXN],cnt,num,sta[MAXN],top;
ll read(){
    ll x=0;
    char ch=getchar();
    while(0>ch || 9<ch) ch=getchar();
    while(0<=ch && ch<=9){x=(x<<1)+(x<<3)+ch-0; ch=getchar();}
    return x;
}
void ade(ll u,ll v){
    to[++tot]=v;
    nxt[tot]=fir[u];
    fir[u]=tot;
}
void Tarjan(ll x){
    low[x]=dfn[x]=++num;
    sta[++top]=x;
    for(ll k=fir[x];k!=-1;k=nxt[k]){
        if(!dfn[to[k]]){
            Tarjan(to[k]);
            low[x]=min(low[x],low[to[k]]);
        }
        else if(!col[to[k]]) low[x]=min(low[x],dfn[to[k]]);
    }
    if(low[x]==dfn[x]){
        col[x]=++cnt;
        while(sta[top]!=x) col[sta[top]]=cnt,--top;
        --top;
    }
}
bool twosat(){
    rep(i,1,2*n) if(!dfn[i]) Tarjan(i);
    rep(i,1,n) if(col[i]==col[i+n]) return 0;
    return 1;
}
int main(){
    memset(fir,-1,sizeof(fir));
    scanf("%lld%lld",&n,&m);
    rep(i,1,m){
        int e=read(),r=read(),t=read(),y=read();
        ade(e+r*n,t+(1-y)*n);
        ade(t+y*n,e+(1-r)*n);
    }
    if(twosat()){
        printf("POSSIBLE
");
        for(int i=1; i<=n; i++)
            printf("%d ",col[i]<col[i+n]);
    }
    else printf("IMPOSSIBLE");
    return 0;
}

 

 

 

 四、一些题目

  这些题目其实和板子区别不大

 一、和平委员会

题目描述

原题来自:POI 2001

根据宪法,Byteland 民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。

此委员会必须满足下列条件:

  • 每个党派都在委员会中恰有 1 个代表,

  • 如果 2 个代表彼此厌恶,则他们不能都属于委员会。

每个党在议会中有 2个代表。代表从 1 编号到 2n。 编号为 2i1 和 2i 的代表属于第 i 个党派。

任务:写一程序读入党派的数量和关系不友好的代表对,计算决定建立和平委员会是否可能,若行,则列出委员会的成员表。

输入格式

第一行有两个非负整数 n 和 m。他们各自表示:党派的数量 n 和不友好的代表对 m。 接下来 m 行,每行为一对整数 a,b,表示代表 a,b 互相厌恶。

输出格式

如果不能创立委员会,则输出信息NIE。若能够成立,则输出包括 n 个从区间 1 到 2n 选出的整数,按升序写出,每行一个,这些数字为委员会中代表的编号。

如果委员会能以多种方法形成,程序可以只输出它们的某一个。

样例

样例输入

3 2
1 3
2 4

样例输出

1
4
5

数据范围与提示

1n8000,0m20000,1a<b2n

嗯,其实只要BB一下建图方案

若取a则必取-b,若取b则必取-a

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define rep(i,a,b) for(int i=a;i<=b;i++)
 6 using namespace std;
 7 int n,m,to[40050],nxt[40050],fir[17000],tot;
 8 int dfn[17000],low[17000],num,cnt,col[17000],sta[17000],top;
 9 void ade(int u,int v){
10     to[++tot]=v;
11     nxt[tot]=fir[u];
12     fir[u]=tot;
13 }
14 void Tarjan(int x){
15     dfn[x]=low[x]=++num;
16     sta[++top]=x;
17     for(int k=fir[x];k;k=nxt[k]){
18         if(!dfn[to[k]]){Tarjan(to[k]); low[x]=min(low[to[k]],low[x]);}
19         else if(!col[to[k]]) low[x]=min(low[x],dfn[to[k]]);
20     }
21     if(low[x]==dfn[x]){
22         col[x]=++cnt;
23         while(sta[top]!=x) col[sta[top]]=cnt,--top;
24         --top;
25     }
26 }
27 bool twosat(){
28     rep(i,1,2*n) if(!dfn[i]) Tarjan(i);
29     rep(i,1,n) if(col[i*2]==col[i*2-1]) return 0;
30     
31     return 1;
32 }
33 int main(){
34     scanf("%d%d",&n,&m);
35     rep(i,1,m){
36         int a,b,c=1,d=1; scanf("%d%d",&a,&b);
37         if(a&1) c=-1; if(b&1) d=-1;
38         ade(a,b-d); ade(b,a-c);
39     }
40     if(twosat()) rep(i,1,n) if(col[i*2]>col[i*2-1]) printf("%d
",i*2-1);else printf("%d
",i*2);
41     else printf("NIE");
42 }

 

 

 

以上是关于2-Sat的主要内容,如果未能解决你的问题,请参考以下文章

模板 2-sat

POJ 3905 Perfect Election(2-sat)

BZOJ1823: [JSOI2010]满汉全席(2-sat)

[luogu4782]模板2-SAT 问题

POJ 2296 Map Labeler(2-sat)

BZOJ1823 [JSOI2010]满汉全席 2-sat