在这篇文章我们提到过sat问题,sat问题是第一个npc问题,具体是这样的SAT全称是satisfiability,他是问对于一个合取范式,是否有一种输入使得他的输出是1,具体点就是类似这样的布尔表达式(x1 or x2 or x3)and(x3 or x4)and(not x1 or x5)对于所有的x是否有一种01取值,使得最后的结果是1。而2-sat问题就是每一个由or连接的子式都只包含两个变量,比如这样(x1 or x2) and (not x3 or x1),2-sat问题是有多项式解法的,而3-sat就是npc问题了,前段时间有人宣称证明了p=np就是因为他自己找到了3-sat的多项式解法,当然最后被证明解法是错误的。。。那么对于2-sat问题解法而言,经典的就是利用强连通分支的算法来解决,最近上的coursera上的algo2有个随机算法也很有趣这里我们也要讲一下。
先来看经典的利用强连通分支的图论解法。我们把每个变量x都拆成2个点,两个点x和~x分别表示这个点取1和这个点取0,所以最后我们就是在这2n个点中选择满足要求的n个点。对于每个子式,假设子式是(x1 or x2),对于2-sat问题而言我们需要每个子式都得1,也就是对于这个子式而言,x1和x2至少要取一个,对应于图中就是,如果我们取~x1就必须取x2,取~x2就必须取x1,所以我们就在图中从~x1到x2和从~x2到x1连两条有向边。同样的如果子式中有not也是类似方法,比如(not x1 or x2)那么就是X1到x2和~x2到~x1两条有向边。一开始的图片构成的图表示的这个式子。
构建好图之后对图求强连通分支,很显然的如果xi和~xi在同一个强连通分支中那么就是不存在解的。之后进行染色判定,强连通分支缩点之后,把所有的边反向,然后按照拓扑排序的顺序遍历节点,如果节点没有被染色,就涂成红色,然后把和这个点互斥的点(所谓互斥的点就是如果x和~x所在的点),以及这个点的子孙都涂成蓝色,这样取出红色的点就是满足条件的解。这里就不证明了,详细的可以看伍昱的《由对称性解2-SAT问题》和赵爽的《2-SAT解法浅析》两篇。看证明的时候注意对称性,就是说如果x,y在同一个连通分支,那么~x,~y也在同一个连通分支,如果x到y有路,那么~y到~x也有路,注意这个对称性的特点的话,那两篇文章里的证明也就不难看懂了。
talk is easy,show me the code,那么让我们看一下代码吧,这里就用poj 3648来举例。
-
#include <cstdio>
-
#include <cstring>
-
-
const int maxn = 10010;
-
int n, m, nxt [maxn ], head [maxn ], pnt [maxn ], ne, e, a, b, a0, b0;
-
char c1, c2;
-
int nnxt [maxn ], nhead [maxn ], npnt [maxn ];
-
int order [maxn ], norder, id [maxn ], v [maxn ];
-
int ans [maxn ], op [maxn ];
-
-
void dfs ( int d ) {
-
v [d ] = 1;
-
for ( int i =head [d ]; i != -1; i =nxt [i ] )
-
if ( !v [pnt [i ] ] )
-
dfs (pnt [i ] );
-
order [norder ++ ] = d;
-
}
-
void ndfs ( int d, int k ) {
-
v [d ] = 1;
-
id [d ] = k;
-
for ( int i =nhead [d ]; i != -1; i =nnxt [i ] )
-
if ( !v [npnt [i ] ] )
-
ndfs (npnt [i ], k );
-
}
-
void addedge ( int s, int t ) {
-
pnt [e ] = t; nxt [e ] = head [s ]; head [s ] = e ++;
-
}
-
void addnedge ( int t, int s ) {
-
npnt [ne ] = s; nnxt [ne ] = nhead [t ]; nhead [t ] = ne ++;
-
}
-
void color ( int d ) {
-
ans [d ] = 2;
-
for ( int i =head [d ]; i != -1; i =nxt [i ] )
-
if ( !ans [pnt [i ] ] )
-
color (pnt [i ] );
-
}
-
int main ( ) {
-
while ( 1 ) {
-
norder = e = ne = 0;
-
memset (head, -1, sizeof head );
-
memset (nhead, -1, sizeof nhead );
-
scanf ( "%d%d", &n, &m );
-
if ( !n &&!m )
-
break;
-
for ( int i = 0; i<m; ++i ) {
-
scanf ( "%d%c%d%c", &a, &c1, &b, &c2 );
-
if (c1 == 'h' )
-
a0 = n +a;
-
else {
-
a0 = a;
-
a += n;
-
}
-
if (c2 == 'h' )
-
b0 = n +b;
-
else {
-
b0 = b;
-
b += n;
-
}
-
addedge (a0, b );
-
addnedge (b, a0 );
-
addedge (b0, a );
-
addnedge (a, b0 );
-
}
-
addedge ( 0, n );
-
&nbs
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow