2-sat

建议初学者先参考:伍昱的《由对称性解2-sat问题》 - 03年IOI国家集训队论文ppt

还有参考http://www.cnblogs.com/silver-bullet/archive/2013/02/18/2915097.html

下面的算法一指暴力枚举,算法二指缩点拓扑。

HDU 1814 Peaceful Commission

题意:2n个人,2i-1 和 2i 选且仅选1人。有m对不和人,不和的两人不能同时被选。输出最小字典序的合法方案。

题解:ppt里的经典例题。由于需要最小字典序,需要暴力2-sat。若i和j不和,显然选i则只能选j',选j则只能选i'。(这里注意,选i'(或j')可以选j(i)也可以选j'(i'))

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 #define N 16010
 8 #define M 40010
 9 struct Edge{
10     int u,v,nxt;
11 }e[M];
12 int head[N],m;
13 void init(){ m=0; memset(head,-1,sizeof(head)); }
14 void addedge(int u,int v){
15     e[m].u=u, e[m].v=v, e[m].nxt=head[u];
16     head[u]=m++;
17 }
18 int c[N];
19 int que[N],tl;// temp
20 bool dfs(int u){
21     if(c[u]==1)return true;
22     if(c[u]==0)return false;
23     c[u]=1;
24     c[u^1]=0;
25     que[tl++]=u;
26     for(int i=head[u]; i!=-1; i=e[i].nxt)
27         if(dfs(e[i].v)==false) return false;
28     return true;
29 }
30 bool solve(int n){
31     memset(c,-1,sizeof(c));
32     for(int i=0; i<2*n; i+=2){
33         if(c[i] != -1) continue;
34         tl = 0;
35         if(!dfs(i)){
36             for(int j=0;j<tl;++j)
37                 c[que[j]] = c[que[j]^1] = -1;
38             if(!dfs(i+1)) return false;
39         }
40     }
41     return true;
42 }
43 int main(){
44     int n,m;
45     while(~scanf("%d%d",&n,&m)){
46         init();
47         for(int i=1;i<=m;i++){
48             int a,b;
49             scanf("%d%d",&a,&b);
50             --a,--b;
51             addedge(a,b^1);
52             addedge(b,a^1);
53         }
54         if(solve(n)){
55             for(int i=0;i<2*n;i++){
56                 if(c[i]==1) printf("%d
",i+1);
57             }
58         }
59         else puts("NIE");
60     }
61     return 0;
62 }
HDU 1814 算法一

POJ 3207 Ikki's Story IV - Panda's Trick

题意:圆上有n个点,m条边,边的端点是那n个点中的2个点。边可以在圆内或圆外,输入保证每个点最多只是一条边的端点。问边是否相交。

题解:圆内和圆外2种情况,可以把边当做2-sat中的点。若2条圆内边相交,则这2条边的圆外边必定相交。即,若i与j相交,则选i只能选j',选j只能选i',选i'只能选j,选j'只能选i。注意(a,b)和(c,d)相交的判断条件是(a-c)*(a-d)*(b-c)*(b-d)<0.

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 #define N 1010
 8 #define M (1010*2010)
 9 struct Edge{
10     int u,v,nxt;
11 }e[M];
12 int head[N],m;
13 void init(){ m=0; memset(head,-1,sizeof(head)); }
14 void addedge(int u,int v){
15     e[m].u=u, e[m].v=v, e[m].nxt=head[u];
16     head[u]=m++;
17 }
18 int c[N];
19 int que[N],tl;// temp
20 bool dfs(int u){
21     if(c[u]==1)return true;
22     if(c[u]==0)return false;
23     c[u]=1;
24     c[u^1]=0;
25     que[tl++]=u;
26     for(int i=head[u]; i!=-1; i=e[i].nxt)
27         if(dfs(e[i].v)==false) return false;
28     return true;
29 }
30 bool solve(int n){
31     memset(c,-1,sizeof(c));
32     for(int i=0; i<2*n; i+=2){
33         if(c[i] != -1) continue;
34         tl = 0;
35         if(!dfs(i)){
36             for(int j=0;j<tl;++j)
37                 c[que[j]] = c[que[j]^1] = -1;
38             if(!dfs(i+1)) return false;
39         }
40     }
41     return true;
42 }
43 int main(){
44     int n,m;
45     int uv[510][2];
46     while(~scanf("%d%d",&n,&m)){
47         init();
48         for(int i=1;i<=m;i++) scanf("%d%d",&uv[i][0],&uv[i][1]);
49         for(int i=1;i<=m;++i){
50             for(int j=i+1;j<=m;++j){
51                 int a=uv[i][0],b=uv[i][1],c=uv[j][0],d=uv[j][1];
52                 if(1ll*(a-c)*(a-d)*(b-c)*(b-d)<0){
53                     int u=i*2-2,v=j*2-2;
54                     addedge(u,v^1);
55                     addedge(v^1,u);
56                     addedge(v,u^1);
57                     addedge(u^1,v);
58                 }
59             }
60         }
61         if(solve(m)) puts("panda is telling the truth...");
62         else puts("the evil panda is lying again");
63     }
64     return 0;
65 }
POJ 3207 算法一
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7 
 8 #define maxn 1010
 9 vector<int>e[maxn],ee[maxn];
10 int dfn[maxn],low[maxn],id,cnt;
11 bool ins[maxn];
12 int st[maxn],top;
13 int belong[maxn];
14 void strongconnect(int x,int fa){
15     dfn[x]=low[x]=++id;
16     st[top++]=x;
17     ins[x] = true;
18     for(int i=e[x].size()-1;i>=0;--i){
19         int v = e[x][i];
20         if(!dfn[v]){
21             strongconnect(v,x);
22             low[x] = min(low[x],low[v]);
23             //if(low[v]>=dfn[u]) cutp[u] = true;
24             //if(low[v]>dfn[u]) cute[u][v] = true;
25         }
26         else if(ins[v]) low[x] = min(low[x],dfn[v]);
27     }
28     if(dfn[x]==low[x]){
29         int u=-1;
30         while(u!=x){
31             u = st[--top];
32             ins[u]=false;
33             belong[u]=cnt;
34         }
35         ++cnt;
36     }
37 }
38 void tarjan(int n){
39     id=cnt=top=0;
40     memset(dfn,0,sizeof(dfn));
41     memset(low,0,sizeof(low));
42     memset(ins,0,sizeof(ins));
43     for(int i=0;i<n;++i)
44         if(!dfn[i]) strongconnect(i,-1);
45 }
46 bool solve(int n){
47     tarjan(n);
48     bool flag = true;
49     for(int i=0;i<n;i+=2)
50         if(belong[i]==belong[i^1]) flag = false;
51     return flag;
52 }
53 int main(){
54     int n,m;
55     int uv[510][2];
56     while(~scanf("%d%d",&n,&m)){
57         for(int i=0;i<=2*m;++i) e[i].clear(),ee[i].clear();
58         for(int i=1;i<=m;i++) scanf("%d%d",&uv[i][0],&uv[i][1]);
59         for(int i=1;i<=m;++i){
60             for(int j=i+1;j<=m;++j){
61                 int a=uv[i][0],b=uv[i][1],c=uv[j][0],d=uv[j][1];
62                 if(1ll*(a-c)*(a-d)*(b-c)*(b-d)<0){
63                     int u=i*2-2,v=j*2-2;
64                     e[u].push_back(v^1);
65                     e[v^1].push_back(u);
66                     e[v].push_back(u^1);
67                     e[u^1].push_back(v);
68                 }
69             }
70         }
71         if(solve(2*m)) puts("panda is telling the truth...");
72         else puts("the evil panda is lying again");
73     }
74     return 0;
75 }
POJ 3207 算法二

HDU 3622 Bomb Game

题意:2n个同半径圆,2i-1和2i 选且仅选1个。输出最大半径使得所有圆之间互相不想交。

题解:若i和j相交,显然选i则只能选j',选j则只能选i'。二分答案。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 #define N 210
 8 #define M (210*210)
 9 #define eps 1e-8
10 struct Edge{
11     int u,v,nxt;
12 }e[M];
13 int head[N],m;
14 void init(){ m=0; memset(head,-1,sizeof(head)); }
15 void addedge(int u,int v){
16     e[m].u=u, e[m].v=v, e[m].nxt=head[u];
17     head[u]=m++;
18 }
19 int c[N];
20 int que[N],tl;// temp
21 bool dfs(int u){
22     if(c[u]==1)return true;
23     if(c[u]==0)return false;
24     c[u]=1;
25     c[u^1]=0;
26     que[tl++]=u;
27     for(int i=head[u]; i!=-1; i=e[i].nxt)
28         if(dfs(e[i].v)==false) return false;
29     return true;
30 }
31 bool solve(int n){
32     memset(c,-1,sizeof(c));
33     for(int i=0; i<2*n; i+=2){
34         if(c[i] != -1) continue;
35         tl = 0;
36         if(!dfs(i)){
37             for(int j=0;j<tl;++j)
38                 c[que[j]] = c[que[j]^1] = -1;
39             if(!dfs(i+1)) return false;
40         }
41     }
42     return true;
43 }
44 struct Point{
45     double x,y;
46 }pnt[210];
47 double dis2(Point a,Point b){
48     double dx = a.x-b.x, dy = a.y-b.y;
49     return dx*dx+dy*dy;
50 }
51 bool jud(int n,double R){
52     init();
53     for(int i=0;i<2*n;++i){
54         for(int j=i+1;j<2*n;++j){
55             if(dis2(pnt[i],pnt[j])<4*R*R-eps && i!=(j^1)){
56                 addedge(i,j^1);
57                 addedge(j,i^1);
58             }
59         }
60     }
61     return solve(n);
62 }
63 int main(){
64     int n;
65     while(~scanf("%d",&n)){
66         for(int i=0;i<n;++i) scanf("%lf%lf%lf%lf",&pnt[i<<1].x,&pnt[i<<1].y,&pnt[i<<1|1].x,&pnt[i<<1|1].y);
67         double l=0,r=1e9;
68         while(l<r-eps){
69             double mid = (l+r)/2;
70             if(jud(n,mid)) l=mid;
71             else r=mid;
72         }
73         printf("%.2lf
",l);
74     }
75     return 0;
76 }
HDU 3622 算法一
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7 
 8 #define maxn 210
 9 #define eps 1e-8
10 vector<int>e[maxn],ee[maxn];
11 int dfn[maxn],low[maxn],id,cnt;
12 bool ins[maxn];
13 int st[maxn],top;
14 int belong[maxn];
15 void strongconnect(int x,int fa){
16     dfn[x]=low[x]=++id;
17     st[top++]=x;
18     ins[x] = true;
19     for(int i=e[x].size()-1;i>=0;--i){
20         int v = e[x][i];
21         if(!dfn[v]){
22             strongconnect(v,x);
23             low[x] = min(low[x],low[v]);
24             //if(low[v]>=dfn[u]) cutp[u] = true;
25             //if(low[v]>dfn[u]) cute[u][v] = true;
26         }
27         else if(ins[v]) low[x] = min(low[x],dfn[v]);
28     }
29     if(dfn[x]==low[x]){
30         int u=-1;
31         while(u!=x){
32             u = st[--top];
33             ins[u]=false;
34             belong[u]=cnt;
35         }
36         ++cnt;
37     }
38 }
39 void tarjan(int n){
40     id=cnt=top=0;
41     memset(dfn,0,sizeof(dfn));
42     memset(low,0,sizeof(low));
43     memset(ins,0,sizeof(ins));
44     for(int i=0;i<n;++i)
45         if(!dfn[i]) strongconnect(i,-1);
46 }
47 bool solve(int n){
48     tarjan(n);
49     for(int i=0;i<n;i+=2)
50         if(belong[i]==belong[i^1]) return false;
51     return true;
52 }
53 struct Point{
54     double x,y;
55 }pnt[210];
56 double dis2(Point a,Point b){
57     double dx = a.x-b.x, dy = a.y-b.y;
58     return dx*dx+dy*dy;
59 }
60 bool jud(int n,double R){
61     for(int i=0;i<=2*n;++i) e[i].clear();
62     for(int i=0;i<2*n;++i){
63         for(int j=i+1;j<2*n;++j){
64             if(dis2(pnt[i],pnt[j])<4*R*R-eps && i!=(j^1)){
65                 e[i].push_back(j^1);
66                 e[j].push_back(i^1);
67             }
68         }
69     }
70     return solve(2*n);
71 }
72 int main(){
73     int n;
74     while(~scanf("%d",&n)){
75         for(int i=0;i<n;++i) scanf("%lf%lf%lf%lf",&pnt[i<<1].x,&pnt[i<<1].y,&pnt[i<<1|1].x,&pnt[i<<1|1].y);
76         double l=0,r=1e9;
77         while(l<r-eps){
78             double mid = (l+r)/2;
79             if(jud(n,mid)) l=mid;
80             else r=mid;
81         }
82         printf("%.2lf
",l);
83     }
84     return 0;
85 }
HDU 3622 算法二

POJ 3683 Priest John's Busiest Day

题意:n个婚礼时间段(S[i],T[i]),每个婚礼有一个“特别庆祝祝福”的时长D[i],“特别庆祝祝福”只会在(S[i],S[i]+D[i])或(T[i]-D[i],T[i])。问如何安排可不错过所有婚礼的“特别庆祝祝福”。

题解:若i和j相交,显然选i则只能选j',选j则只能选i'。这题要求任意方案,只需要再做反的拓扑排序即可。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <queue>
  6 using namespace std;
  7 
  8 #define maxn 2010
  9 #define maxm (2010*2010)
 10 struct Edge{
 11     int v,nxt;
 12 }e[maxm],ee[maxm];
 13 int head[maxn],hh[maxn];
 14 int em,eem;
 15 void init(){em=0,eem=0;memset(head,-1,sizeof(head));memset(hh,-1,sizeof(hh));}
 16 void addedge(int u,int v){
 17     e[em].v=v,e[em].nxt=head[u];
 18     head[u]=em++;
 19 }
 20 void addedge2(int u,int v){
 21     ee[eem].v=v,ee[eem].nxt=hh[u];
 22     hh[u]=eem++;
 23 }
 24 int dfn[maxn],low[maxn],id,cnt;
 25 bool ins[maxn];
 26 int st[maxn],top;
 27 int belong[maxn];
 28 int nn[maxn];
 29 void strongconnect(int x,int fa){
 30     dfn[x]=low[x]=++id;
 31     st[top++]=x;
 32     ins[x] = true;
 33     for(int i=head[x];i!=-1;i=e[i].nxt){
 34         int v = e[i].v;
 35         if(!dfn[v]){
 36             strongconnect(v,x);
 37             low[x] = min(low[x],low[v]);
 38             //if(low[v]>=dfn[u]) cutp[u] = true;
 39             //if(low[v]>dfn[u]) cute[u][v] = true;
 40         }
 41         else if(ins[v]) low[x] = min(low[x],dfn[v]);
 42     }
 43     if(dfn[x]==low[x]){
 44         int u=-1;
 45         nn[cnt]=0;
 46         while(u!=x){
 47             u = st[--top];
 48             ins[u]=false;
 49             belong[u]=cnt;
 50             nn[cnt]++;
 51         }
 52         ++cnt;
 53     }
 54 }
 55 void tarjan(int n){
 56     id=cnt=top=0;
 57     memset(dfn,0,sizeof(dfn));
 58     memset(low,0,sizeof(low));
 59     memset(ins,0,sizeof(ins));
 60     for(int i=0;i<n;++i)
 61         if(!dfn[i]) strongconnect(i,-1);
 62 }
 63 bool vise[maxn][maxn];
 64 bool solve(int n){
 65     tarjan(n);
 66     for(int i=0;i<n;i+=2)
 67         if(belong[i]==belong[i^1]) return false;
 68     int in[maxn];
 69     memset(in,0,sizeof(in));
 70     memset(vise,false,sizeof(vise));
 71     for(int i=0;i<n;++i){
 72         for(int j=head[i];j!=-1;j=e[j].nxt){
 73             int u = belong[i], v = belong[e[j].v];
 74             if(u==v || vise[v][u]) continue;
 75             addedge2(v,u);
 76             vise[v][u]=true;
 77             ++in[u];
 78         }
 79     }
 80     queue<int>que;
 81     memset(ins,false,sizeof(ins));
 82     int idx=0;
 83     for(int i=0;i<cnt;++i) if(in[i]==0) que.push(i), dfn[i]=idx++;
 84     while(!que.empty()){
 85         int u = que.front(); que.pop();
 86         for(int i=hh[u];i!=-1;i=ee[i].nxt){
 87             int v = ee[i].v;
 88             if(--in[v]==0){
 89                 que.push(v);
 90                 dfn[v]=idx++;
 91             }
 92         }
 93     }
 94     return true;
 95 }
 96 int main(){
 97     int n;
 98     int times[2010];
 99     int cost[1010];
100     while(~scanf("%d",&n)){
101         for(int i=0;i<n;++i){
102             int t1,t2,t3,t4,t5;
103             scanf("%d:%d%d:%d%d",&t1,&t2,&t3,&t4,&t5);
104             times[i<<1]=t1*60+t2, times[i<<1|1]=t3*60+t4-t5;
105             cost[i] = t5;
106         }
107         init();
108         for(int i=0;i<2*n;++i){
109             for(int j=i+1;j<2*n;++j){
110                 if(i==(j^1)) continue;
111                 int a,b,c,d;
112                 a = times[i], b = times[i]+cost[i/2];
113                 c = times[j], d = times[j]+cost[j/2];
114                 if(!(b<=c || d<=a)){
115                     addedge(i,j^1);
116                     addedge(j,i^1);
117                 }
118             }
119         }
120         if(solve(2*n)){
121             puts("YES");
122             for(int i=0;i<2*n;i+=2){
123                 int t1,t2;
124                 if(dfn[belong[i]]<dfn[belong[i^1]])
125                     t1 = times[i], t2 = t1+cost[i/2];
126                 else t1 = times[i^1], t2 = t1+cost[i/2];
127                 printf("%02d:%02d %02d:%02d
",t1/60,t1%60,t2/60,t2%60);
128             }
129         }else puts("NO");
130     }
131     return 0;
132 }
POJ 3683 算法二
原文地址:https://www.cnblogs.com/nextbin/p/3949687.html