首页 > 技术文章 > 【笔记】2-SAT (tarjan)

JCNL666 2019-04-24 22:46 原文

TWO-SAT略解:

Part 1 (引入):

其实2-SAT是用来解决一系列类似如果我怎么怎么样,他就怎么怎么怎么样的东西,具体来说就是只有两种状态,两种状态不能兼得。

举个例子:

\(A\) 说如果 \(B\) 去参加群架,那我也得去。

\(B\) 说如果有群架,那我跟 \(C\) 去一个就可以了。

\(C\) 说如果有群架,那我肯定去。

发现这道题其实咱小学就会做了只要画个表格,填个表格就行了。

那考虑怎么让计算机快速的求出这个东西。

Part 2 (口糊):

还是以上面那个例子。

对于 \(A\) 说的话,咱可以这样连边: \(B->A\)

对于 \(B\) 说的话,咱这么连 \(B'->C,C'->B\)

对于 \(C\) 说的话,咱这么连 \(C'->C\)

(所有的'表示这个点不选)

然后考虑怎么做这个玩意儿。

我们可以对它跑一边tarjan求出所有强连通分量,然后按照拓扑序判断每个点选还是不选。

为什么是拓扑序呢?

考虑\(x->x'\),那么肯定如果 \(x\) 选了 \(x'\) 也得选,但是按照咱的说法,这条边的意思是:\(x\) 这个点一定不选。那就矛盾了,只能选拓扑序大的

这里说一下关于tarjan的小技巧,tarjan出来给出的就是反拓扑序,所以其实我们要找的是强连通分量编号小的。

Part 3 (代码):

inline bool two_sat(){
    for(int i=1;i<=n;++i)
        if(scc[i]==scc[i+n]) return false;
    return true;
}

尴尬,好短啊。。。

Part 4 (例题):

JSOI原题

发现这题其实是个垃圾题,每种材料只有两种做法,跑一遍裸的2-SAT

模板题

这题可一用来练输出方案。

CF875C:

这题是真的好题,乍一眼看不会,其实是2-SAT,可以参考这篇博文:

CR我都帮你安利了还不帮我推销推销

大概就这样了,最后一题博主的题解:(还没写,到时候补上。。。)

推荐阅读