【笔记】2-SAT (tarjan)
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 (例题):
发现这题其实是个垃圾题,每种材料只有两种做法,跑一遍裸的2-SAT
模板题:
这题可一用来练输出方案。
这题是真的好题,乍一眼看不会,其实是2-SAT,可以参考这篇博文:
大概就这样了,最后一题博主的题解:(还没写,到时候补上。。。)