{TwoSAT}

今天学习图论的时候,碰到了2sat问题

虽然不是很难理解,感觉很精妙 ▄█?█●

用的LRJ白书上的模板。

套路如下:

   2 - SAT就是2判定性问题,是一种特殊的逻辑判定问题。

  选择的置为1,未选的置为0

  对于2SAT,每组矛盾都会有四种情况(2*2),题目会限制一种不成立,我们要做的就是找出这一种,用逻辑连接词表示出来,然后取反,加边即可。

  具体过程白书上p324有讲,这里就不说了。

  下面附三道题,做做就知道套路了

Party

HDU - 3062

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1010;
 4 struct TwoSAT
 5 {
 6     int n;
 7     vector<int> G[maxn<<1];
 8     bool mark[maxn<<1];
 9     int s[maxn<<1],c;
10
11     bool dfs(int x)
12     {
13         if(mark[x^1]) return false;
14         if(mark[x]) return true;
15         mark[x]=true;
16         s[c++]=x;
17         for(int i=0;i<G[x].size();i++)
18             if(!dfs(G[x][i])) return false;
19         return true;
20     }
21
22     void init(int n)
23     {
24         this->n=n;
25         for(int i=0;i<n*2;i++) G[i].clear();
26         memset(mark,0,sizeof(mark));
27     }
28
29     void add_clause(int x,int xv,int y,int yv)
30     {
31         x=x*2+xv;
32         y=y*2+yv;
33         G[x^1].push_back(y);
34         G[y^1].push_back(x);
35     }
36
37     bool solve()
38     {
39         for(int i=0;i<n*2;i+=2)
40             if(!mark[i]&&!mark[i^1])
41             {
42                 c=0;
43                 if(!dfs(i))
44                 {
45                     while(c>0) mark[s[--c]]=false;
46                     if(!dfs(i+1)) return false;
47                 }
48             }
49         return true;
50     }
51 }solver;
52 int n,m;
53 int age[maxn],sum;
54
55
56 int main()
57 {
58     while(scanf("%d%d",&n,&m)!=EOF)
59     {
60         int x,y,a,b;
61         solver.init(n);
62         for(int i=0;i<m;i++)
63         {
64            scanf("%d%d%d%d",&a,&b,&x,&y);
65            solver.add_clause(a,x^1,b,y^1);
66         }
67         if(solver.solve()) puts("YES");
68         else puts("NO");
69     }
70     return 0;
71
72
73 }

Now or later

UVALive - 3211

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=2010;
 4 struct TwoSAT
 5 {
 6     int n;
 7     vector<int> G[maxn<<1];
 8     bool mark[maxn<<1];
 9     int s[maxn<<1],c;
10
11     bool dfs(int x)
12     {
13         if(mark[x^1]) return false;
14         if(mark[x]) return true;
15         mark[x]=true;
16         s[c++]=x;
17         for(int i=0;i<G[x].size();i++)
18             if(!dfs(G[x][i])) return false;
19         return true;
20     }
21
22     void init(int n)
23     {
24         this->n=n;
25         for(int i=0;i<n*2;i++) G[i].clear();
26         memset(mark,0,sizeof(mark));
27     }
28
29     void add_clause(int x,int xv,int y,int yv)
30     {
31         x=x*2+xv;
32         y=y*2+yv;
33         G[x^1].push_back(y);
34         G[y^1].push_back(x);
35     }
36
37     bool solve()
38     {
39         for(int i=0;i<n*2;i+=2)
40             if(!mark[i]&&!mark[i^1])
41             {
42                 c=0;
43                 if(!dfs(i))
44                 {
45                     while(c>0) mark[s[--c]]=false;
46                     if(!dfs(i+1)) return false;
47                 }
48             }
49         return true;
50     }
51 }solver;
52 int n;
53 int T[maxn][2];
54
55 bool test(int diff)
56 {
57     solver.init(n);
58     for(int i=0;i<n;i++) for(int a=0;a<2;a++)
59         for(int j=i+1;j<n;j++) for(int b=0;b<2;b++)
60         if(abs(T[i][a]-T[j][b])<diff) solver.add_clause(i,a^1,j,b^1);
61     return solver.solve();
62 }
63
64 int main()
65 {
66     while(scanf("%d",&n)==1&&n)
67     {
68         int L=0,R=0;
69         for(int i=0;i<n;i++) for(int a=0;a<2;a++)
70         {
71             scanf("%d",&T[i][a]);
72             R=max(R,T[i][a]);
73         }
74         while(L<R)
75         {
76             int M=(R-L+1)/2+L;
77             if(test(M)) L=M;
78             else R=M-1;
79         }
80         printf("%d\n",L);
81     }
82     return 0;
83
84 }

Astronauts

UVALive - 3713

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=100010;
 4 struct TwoSAT
 5 {
 6     int n;
 7     vector<int> G[maxn<<1];
 8     bool mark[maxn<<1];
 9     int s[maxn<<1],c;
10
11     bool dfs(int x)
12     {
13         if(mark[x^1]) return false;
14         if(mark[x]) return true;
15         mark[x]=true;
16         s[c++]=x;
17         for(int i=0;i<G[x].size();i++)
18             if(!dfs(G[x][i])) return false;
19         return true;
20     }
21
22     void init(int n)
23     {
24         this->n=n;
25         for(int i=0;i<n*2;i++) G[i].clear();
26         memset(mark,0,sizeof(mark));
27     }
28
29     void add_clause(int x,int xv,int y,int yv)
30     {
31         x=x*2+xv;
32         y=y*2+yv;
33         G[x^1].push_back(y);
34         G[y^1].push_back(x);
35     }
36
37     bool solve()
38     {
39         for(int i=0;i<n*2;i+=2)
40             if(!mark[i]&&!mark[i^1])
41             {
42                 c=0;
43                 if(!dfs(i))
44                 {
45                     while(c>0) mark[s[--c]]=false;
46                     if(!dfs(i+1)) return false;
47                 }
48             }
49         return true;
50     }
51 }solver;
52 int n,m;
53 int age[maxn],sum;
54
55 bool check(int x)
56 {
57     return age[x]*n<sum;
58 }
59
60 int main()
61 {
62     while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
63     {
64         sum=0;
65         for(int i=0;i<n;i++) {
66             scanf("%d",&age[i]);
67             sum+=age[i];
68         }
69         solver.init(n);
70         for(int i=0;i<m;i++)
71         {
72             int a,b;
73             scanf("%d%d",&a,&b);
74             a--;b--;
75             solver.add_clause(a,1,b,1);
76             if(check(a)==check(b)) solver.add_clause(a,0,b,0);
77         }
78         if(!solver.solve()) printf("No solution\n");
79         else {
80             for(int i=0;i<n;i++)
81             if(solver.mark[i*2]) printf("C\n");
82             else if(check(i)) puts("B");
83             else puts("A");
84         }
85     }
86     return 0;
87 }

基本都一样的套路。。。

时间: 2024-10-12 17:33:02

{TwoSAT}的相关文章

URAL 2089 Experienced coach Twosat

Description Misha trains several ACM teams at the university. He is an experienced coach, and he does not underestimate the meaning of friendly and collaborative atmosphere during training sessions. It used to be that way, but one of the teams happen

hdu3622(二分+two-sat)

传送门:Bomb Game 题意:给n对炸弹可以放置的位置(每个位置为一个二维平面上的点),每次放置炸弹是时只能选择这一对中的其中一个点,每个炸弹爆炸的范围半径都一样,控制爆炸的半径使得所有的爆炸范围都不相交(可以相切),求解这个最大半径. 分析:二分距离,由two-sat判可行性,建图时对于每两个炸弹的两个位置,互相判断一下是否冲突,冲突就建边. #include <cstdio> #include <cstring> #include <string> #inclu

poj3678(two-sat)

传送门:Katu Puzzl 题意:n个点,点的取值可以是0或者1.m条边,有权值,有运算方式(并,或,异或),要求和这条边相连的两个点经过边上的运算后的结果是边的权值.问你有没有可能把每个点赋值满足所有边的要求. 分析:每个点必须取一个值满足所有限制条件,明显的two-sat模型. AND 结果为1:建边 ~a->a,~b->b (两个数必须全为1) AND 结果为0:建边 b->~a,a->~b (两个数至少有一个为0) OR 结果为1:建边 ~a->b,~b->a

TwoSAT算法模板

该模板来自大白书 [解释] 给多个语句,每个语句为“ Xi为真(假) 或者 Xj为真(假)” 每个变量和拆成两个点 2*i为假, 2*i+1为真 “Xi为真 或 Xj为真”  等价于 “Xi为假 –>  Xj为真”. DFS算法没有回溯过程. [函数说明] 模板bfs函数在模板外一般用不到 void init(int n) :初始化 void add(int x,int xval,int y,int yval) :添加边,x,y为节点编号,xval=1表示真,xval=0表示假,yval同理 b

hdu3062(two-sat)

传送门:Party 题意:有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席.在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的.有没有可能会有n 个人同时列席? 分析:two-sat裸题,对于有仇恨的两对夫妻u,v,连边u->v'和v->u':建好图直接tarjan缩点判断每对夫妻[i,i']是否属于一个强连通块内即可. #include <cstdio> #include <cstring&g

Gym 101201F Illumination (Two-Sat)

题意:一个n*n的房子,有很多灯,每个格子只能被上下方向照一次.左右方向照一次,每个灯可以选择上下或是左右照,照明长度以自身位置为中心,占用2*r+1个格子.问能否安排一种方案,使所有格子满足条件. 析:典型的Two-Sat,对于行来说,如果两个能够交叉,那么他们不能都是左右,对于列也是一样. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include &l

poj3207(two-sat)

传送门:Ikki's Story IV - Panda's Trick 题意:给定一个圆,圆上一些点.两点一线.现给出一些线,这些线可以在圆内连起来,也可以在圆外.问有没有可能所有的线画完且不出现相交. 分析:对于每条线,要么在圆外,要么在圆内,且不可同时满足,只能两者取一,判断这M条线是否合法,也就是M条线不冲突,这就是典型的2-sat问题了. 将每条线在圆内当成一点i,在圆外当成一点i',对于两条线,如果在园内同时在圆内,那么必定也不能同时在园外,则有边(i, j') .(j ,i').(i

two-sat hdu3062 UVALive 3211

2-sat就是给定形如 x=xval or y=yval的若干约数,求是否存在全部满足. 这是一种dfs的算法,参考大白书 hdu3062 基本上是模板题吧,xval和yval都告诉你了. #include<bits/stdc++.h> using namespace std; const int N=(int)2e3+10; int n,m; int mark[N],s[N],top=0; vector<int> g[N]; bool dfs(int x) { if(mark[x

LA 3211 飞机调度(2—SAT)

https://vjudge.net/problem/UVALive-3211 题意: 有n架飞机需要着陆,每架飞机都可以选择“早着陆”和“晚着陆”两种方式之一,且必须选择一种,第i架飞机的早着陆时间为E,晚着陆时间为L,不得在其他时间着陆.你的任务是为这些飞机安排着陆方式,使得整个着陆计划尽量安全.换句话说,如果把所有飞机的实际着陆时间按照从早到晚的顺序排列,相邻两个着陆时间间隔的最小值. 思路: 二分查找最大值P,每次都用2—SAT判断是否可行. 1 #include<iostream>