一、关于模型:
一个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 2Sample 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 do26: begin
27: y:=now^.data;28: if dfn[y]=0 then29: begin
30: tarjan(y);31: low[x]:=min(low[x],low[y]);32: end else33: if loc[y]<>0 then low[x]:=min(low[x],dfn[y]);34: now:=now^.next;35: end;
36: if low[x]=dfn[x] then37: begin
38: inc(color);39: loctmp:=loc[x];40: for j:=loctmp to quesum do41: 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 do53: if col[i]=col[i+m] then exit(false);54: exit(true);55: end;
56: begin
57: while not eof do58: 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 do68: 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 do73: for j:=i+1 to m do74: 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]))then76: begin
77: insert(i+m,j);insert(i,j+m);78: end;
79: end;
80: for i:=1 to m*2 do81: if dfn[i]=low[i] then82: 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 XORSample Output
YESHint
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 do24: begin
25: y:=now^.data;26: if dfn[y]=0 then27: begin
28: tarjan(y);29: low[x]:=min(low[x],low[y]);30: end else31: if loc[y]<>0 then low[x]:=min(low[x],dfn[y]);32: now:=now^.next;33: end;
34: if low[x]=dfn[x] then35: begin
36: inc(color);37: loctmp:=loc[x];38: for j:=loctmp to quesum do39: 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 do49: 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 do58: begin
59: read(x);read(y);read(c);readln(s);60: inc(x);inc(y);61: if s=' XOR' then62: if c=1 then63: 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' then73: if c=1 then74: 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' then83: if c=0 then84: 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 do100: if dfn[i]=0 then tarjan(i);101: if ok then writeln('YES') else writeln('NO');102: end.