【Algorithm】2Sat模型

一、关于模型:

一个2-SAT模型应该是一个满足以下的条件的满足性问题:

1、该模型中存在2n个可以分成n组的元素,每组两个元素

2、每组元素中,选择了其中一个元素,另外一个元素就不能被选择。这两个元素记为a和!a

3、该模型中的元素之间存在一些关系,且这些关系是对称的。(除非是同一组元素中的关系,这些关系限定了“必须选择”该组中的某一个元素,可能单独出现)

满足上述条件,要求在满足给定关系的情况下在每组元素中选出一个元素的问题称为2-SAT问题。问是否存在即2-SAT判定问题,当然也可以求出一组可行解。

当你看见一道题目,一些事物只有唯一且互斥的两种选择(比如两种取值,两种连接方式等等),那么可以分析下题目给出的条件是否满足对称性,若满足则可通过构图将题目转化成2-SAT问题了。

二、关于构图

要解2-SAT问题或者完成判定问题,首要的任务是构图。从《由对称性解2-SAT问题》这篇论文里,我们可以知道,构图的关键是找到冲突。

若a和b冲突,即选a时不能选b,那么选a时必须选!b(因为不选b就必须选!b,这是一个2-SAT问题必须满足的条件),那么我们就连边<a,!b>。同样的道理,如果选了b,那么就不能选a,必须选!a,所以连边<b,!a>。这样的连边,显然是对称的。具体的例子可以看上面的论文。

总之,弄清楚一点:如果存在边<a,b>,那么表示选择a时必须选择b。只要将所有这种“必须”关系都变成边,然后再判定2-sat或者求解2-sat,就能解决2-SAT问题了。

三、关于解2-SAT

方法是,对原图求一次强连通分量,然后看每组中的两个点是否属于同一个强连通分量,如果存在这种情况,那么无解

然后对于缩点后的图G',我们将G'中所有边转置。进行拓扑排序。
对于缩点后的所有点,我们先预处理求出所有冲突顶点。例如缩点后Ai所在强连通分支的ID
为id[ Ai] ,同理~Ai在 id[ ~Ai ],所以冲突顶点conflict[ id[Ai] ]=conflict[ id[~Ai] ];同理conflict[ id[~Ai] ]=conflict[ id[Ai] ];

设缩点后有Nscc个点。然后对拓扑序进行染色,初始化所有点color均为未着色,顺序遍历得到的拓扑序列,对于未着色的点x,将x染成红色,同时将所有与x矛盾的点conflic[x]染成蓝色。

2-sat的一组解就等价于所有缩点后点颜色为红色的点,也就是color[ id[i] ]=RED的所有点。

四、例题

【Poj3207】【Ikki's Story IV - Panda's Trick】

Description

liympanda, one of Ikki’s friend, likes playing games with Ikki. Today after minesweeping with Ikki and winning so many times, he is tired of such easy games and wants to play another game with Ikki.

liympanda has a magic circle and he puts it on a plane, there are n points on its boundary in circular border: 0, 1, 2, …, n − 1. Evil panda claims that he is connecting m pairs of points. To connect two points, liympanda either places the link entirely inside the circle or entirely outside the circle. Now liympanda tells Ikki no two links touch inside/outside the circle, except on the boundary. He wants Ikki to figure out whether this is possible…

Despaired at the minesweeping game just played, Ikki is totally at a loss, so he decides to write a program to help him.

Input

The input contains exactly one test case.

In the test case there will be a line consisting of of two integers: n and m (n ≤ 1,000, m ≤ 500). The following m lines each contain two integers ai and bi, which denote the endpoints of the ith wire. Every point will have at most one link.

Output

Output a line, either “panda is telling the truth...” or “the evil panda is lying again”.

Sample Input

4 2
0 1
3 2

Sample Output

panda is telling the truth...
  【Solution】
        最基础的2-sat。对每条边的两种状态算一组,通过是否相交的矛盾来来连边。 
        最后进行判定即可。
   【Code】
  1: Program PandaTrick(input,output);
  2:   type point=^node;
  3:        node=record
  4:          data:longint;
  5:          next:point;
  6:   end;
  7:   var map:array[0..2000]of point;
  8:       dfn,low,que,loc,col:array[0..2000]of longint;
  9:       x,y:array[1..2000]of longint;
 10:       i,n,m,q,j,t,time,quesum,loctmp,color:longint;
 11:   Procedure insert(x,y:longint);
 12:     var p:point;
 13:     begin
 14:       new(p);p^.data:=y;p^.next:=map[x];map[x]:=p;
 15:       new(p);p^.data:=x;p^.next:=map[y];map[y]:=p;
 16:     end;
 17:   procedure tarjan(x:longint);
 18:     var now:point;j,y:longint;
 19:     function min(a,b:longint):longint;
 20:       begin if a<b then exit(a) else exit(b);end;
 21:     begin
 22:       inc(time);low[x]:=time;dfn[x]:=time;
 23:       inc(quesum);que[quesum]:=x;loc[x]:=quesum;
 24:       now:=map[x];
 25:       while now<>nil do
 26:         begin
 27:           y:=now^.data;
 28:           if dfn[y]=0 then
 29:             begin
 30:               tarjan(y);
 31:               low[x]:=min(low[x],low[y]);
 32:             end       else
 33:           if loc[y]<>0 then low[x]:=min(low[x],dfn[y]);
 34:           now:=now^.next;
 35:         end;
 36:       if low[x]=dfn[x] then
 37:         begin
 38:           inc(color);
 39:           loctmp:=loc[x];
 40:           for j:=loctmp to quesum do
 41:             begin
 42:               col[que[j]]:=color;
 43:               loc[que[j]]:=0;
 44:             end;
 45:           quesum:=loctmp-1;
 46:         end;
 47:     end;
 48:   procedure swap(var a,b:Longint);
 49:     var t:longint;begin t:=a;a:=b;b:=t;end;
 50:   Function ok:boolean;
 51:     begin
 52:       for i:=1 to m do
 53:         if col[i]=col[i+m] then exit(false);
 54:       exit(true);
 55:     end;
 56:   begin
 57:     while not eof do
 58:       begin
 59:         readln(n,m);
 60:         fillchar(loc,sizeof(loc),0);
 61:         fillchar(col,sizeof(col),0);
 62:         fillchar(map,sizeof(map),0);
 63:         fillchar(dfn,sizeof(dfn),0);
 64:         fillchar(low,sizeof(low),0);
 65:         fillchar(que,sizeof(que),0);
 66:         time:=0;quesum:=0;color:=0;
 67:         for i:=1 to m do
 68:           begin
 69:             readln(x[i],y[i]);
 70:             if x[i]>y[i] then swap(x[i],y[i]);
 71:           end;
 72:         for i:=1 to m do
 73:           for j:=i+1 to m do
 74:             begin
 75:               if((x[j]>x[i])and(x[j]<y[i])and(y[j]>y[i]))or((x[i]>x[j])and(x[i]<y[j])and(y[i]>y[j]))then
 76:                 begin
 77:                   insert(i+m,j);insert(i,j+m);
 78:                 end;
 79:            end;
 80:         for i:=1 to m*2 do
 81:           if dfn[i]=low[i] then
 82:             tarjan(i);
 83:         if ok then writeln('panda is telling the truth...') else writeln('the evil panda is lying again');
 84:       end;
 85:   end.
 86: 

【Poj 3678】Katu Puzzle

Description

Katu Puzzle is presented as a directed graph G(V, E) with each edge e(a, b) labeled by a boolean operator op (one of AND, OR, XOR) and an integer c (0 ≤ c ≤ 1). One Katu is solvable if one can find each vertex Vi a value Xi (0 ≤ Xi ≤ 1) such that for each edge e(a, b) labeled by op and c, the following formula holds:

Xa op Xb = c

Given a Katu Puzzle, your task is to determine whether it is solvable.

Input

The first line contains two integers N (1 ≤ N ≤ 1000) and M,(0 ≤ M ≤ 1,000,000) indicating the number of vertices and edges.
The following M lines contain three integers a (0 ≤ a < N), b(0 ≤ b < N), c and an operator op each, describing the edges.

Output

Output a line containing "YES" or "NO".

Sample Input

4 4
0 1 1 AND
1 2 1 OR
3 2 0 AND
3 0 0 XOR

Sample Output

YES

Hint

X0 = 1, X1 = 1, X2 = 0, X3 = 1.

【Solution】

每个点只有0,1两种值,并且和边对面的点有约束条件,所以可以转化为2-sat问题。

令i表示点i去0,i + n表示点i取1。

点i与点j and 等于0时。 i + n -> j, j + n -> i

and 等于 1时,要求i和j必须为1,不能为0.

不能为0的处理时:如果i取0,那么i就要取1.

即:i -> i + n。

这个题连边时候需要很多注意。

【Code】

  1: Program Katu(input,output);
  2:   type point=^node;
  3:        node=record
  4:          data:longint;
  5:          next:point;
  6:   end;
  7:   var map:array[1..2000]of point;
  8:       dfn,low,que,loc,col:array[1..2000]of longint;
  9:       x,y,i,n,m,q,j,c,t,time,quesum,loctmp,color:longint;s:string;
 10:   procedure insert(x,y:longint);
 11:     var P:point;
 12:     begin
 13:       new(p);p^.data:=y;p^.next:=map[x];map[x]:=p;
 14:     end;
 15:   procedure tarjan(x:longint);
 16:     var now:point;j,y:longint;
 17:     function min(a,b:longint):longint;
 18:       begin if a<b then exit(a) else exit(b);end;
 19:     begin
 20:       inc(time);low[x]:=time;dfn[x]:=time;
 21:       inc(quesum);que[quesum]:=x;loc[x]:=quesum;
 22:       now:=map[x];
 23:       while now<>nil do
 24:         begin
 25:           y:=now^.data;
 26:           if dfn[y]=0 then
 27:             begin
 28:               tarjan(y);
 29:               low[x]:=min(low[x],low[y]);
 30:             end       else
 31:           if loc[y]<>0 then low[x]:=min(low[x],dfn[y]);
 32:           now:=now^.next;
 33:         end;
 34:       if low[x]=dfn[x] then
 35:         begin
 36:           inc(color);
 37:           loctmp:=loc[x];
 38:           for j:=loctmp to quesum do
 39:             begin
 40:               col[que[j]]:=color;
 41:               loc[que[j]]:=0;
 42:             end;
 43:           quesum:=loctmp-1;
 44:         end;
 45:     end;
 46:   Function Ok:boolean;
 47:     begin
 48:       for i:=1 to n do
 49:         if col[i]=col[i+n] then exit(false);
 50:       exit(true);
 51:     end;
 52:   begin
 53:     assign(input,'Katu.in');reset(input);
 54:     assign(output,'Katu.out');rewrite(output);
 55:     readln(n,m);
 56:     fillchar(map,sizeof(map),0);
 57:     for i:=1 to m do
 58:       begin
 59:         read(x);read(y);read(c);readln(s);
 60:         inc(x);inc(y);
 61:         if s=' XOR' then
 62:           if c=1 then
 63:             begin
 64:               insert(x,y+n);insert(y+n,x);
 65:               insert(x+n,y);insert(y,x+n);
 66:             end
 67:           else
 68:             begin
 69:               insert(x,y);insert(y,x);
 70:               insert(x+n,y+n);insert(y+n,x+n);
 71:             end;
 72:         if s=' AND' then
 73:           if c=1 then
 74:             begin
 75:               insert(x+n,y+n);insert(y+n,x+n);
 76:               insert(x,x+n);insert(y,y+n);
 77:             end
 78:           else
 79:             begin
 80:               insert(x+n,y);insert(y+n,x);
 81:             end;
 82:         if s=' OR' then
 83:           if c=0 then
 84:             begin
 85:               insert(x,y);insert(y,x);
 86:               insert(x+n,x);insert(y+n,y);
 87:             end
 88:           else
 89:             begin
 90:               insert(x,y+n);insert(y,x+n);
 91:             end;
 92:       end;
 93:     fillchar(loc,sizeof(loc),0);
 94:     fillchar(col,sizeof(col),0);
 95:     fillchar(dfn,sizeof(dfn),0);
 96:     fillchar(low,sizeof(low),0);
 97:     fillchar(que,sizeof(que),0);
 98:     quesum:=0;color:=0;time:=0;
 99:     for i:=1 to n*2 do
100:       if dfn[i]=0 then tarjan(i);
101:     if ok then writeln('YES') else writeln('NO');
102:   end.
原文地址:https://www.cnblogs.com/Loongint/p/2215451.html