2 – SAT 总结
来源:互联网 发布:淘宝禁止卖电子书了 编辑:程序博客网 时间:2024/06/04 23:55
2 – SAT 就是2判定性问题,是一种特殊的逻辑判定问题。
例,n对东西,每对只能选一个(i0或i1),不能不选。即:A or _A = 1 , A xor _A = 1
还存在一些约束关系(i0,j0),表示i0不能跟j0一起选。那需连边
i0-> j1 如果选i0的话必须选j1
j0-> i1如果选j0的话必须选i1
表示了一种递推的关系:选哪个必选哪一个
一般题目给的都是一对一对的东西,二选一,不能不选。本身那几对东西是没有关系的,然后题目会定义一些规则,或者我们自己加入条件(如二分的参数),使对与对之间有了一些约束关系: 1)选a必选b a->b
2)a必选 _a->a
对这个新图求SCC,同一SCC的要么全选,要么都不选。
如果发现a,_a在同一SCC,表明矛盾了。
不矛盾的话,对缩点后的图按照自底向上选择一个还未被标记的点,标记选上,然后把它的对立点及其前代都删除。
(选择一个点要把它及其所有后代都选上。不选一个点,要把它及其前代都不选。)
算法流程:
1、建图,求SCC。若某一个点,a与_a在同一个SCC,则无解,退出;
2、对求得的缩点,用原来的反图建一个DAG;
3、TopoSort得到一个原图的自底向上的序列,点是缩点后的SCC,没必要原来的每个点;
4、从左往右,对整个序列的点,如果未标记删除,就标记选它,同时把这个SCC里的所有原来的点的对立点及其各自的后代(因为是反图)都标记删除;
5、重复上面过程,最后被标记选上的SCC内的点都是选择的点。其个数=n
虽然被删除的点也是n个,但是他们可能会存在冲突关系。而标记选上的点间是相容的。
挺多用二分的。对于二分设定的参数,可能建图时要考虑这个条件,如POJ 2749
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
POJ 2749 Building roads
题意:给出n个牛棚、两个特殊点S1,S2的坐标。S1、S2直连。牛棚只能连S1或S2
还有,某些牛棚只能连在同一个S,某些牛棚不能连在同一个S
求使最长的牛棚间距离最小 距离是曼哈顿距离
使最大值最小,二分的经典应用
二分枚举最大值limit,然后重新构图,用2-SAT判定可行性
用布尔变量Xi表示第i个牛棚连到S1,~Xi表示连到S2
检查每一个约束条件,构图:
1.hate关系的i,j Xi->~Xj ~Xi->Xj Xj->~Xi ~Xj->Xi
2.friend关系的i,j Xi->Xj ~Xi->~Xj Xj->Xi ~Xj->~Xi
接下来的也要检查,因为引入参数,就是多了约束条件了
这四种情况就是i,j到达对方的所有情况了
3.dist(i,S1)+dist(S1,j)>limit Xi->~Xj Xj->Xi
4.dist(i,S2)+dist(S2,j)>limit ~Xi->Xj ~Xj->Xi
5.dist(i,S1)+dist(S1,S2)+dist(S2,j)>limit Xi->Xj ~Xj->~Xi
5.dist(i,S2)+dist(S2,S1)+dist(S1,j)>limit ~Xi->~Xj Xj->Xi
然后求SCC 判断Xi与~Xi是否在同一个SCC中,是的话就有矛盾
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 500;
const int INF = 1000000000;
int N,A,B;
struct Two
{
int x,y;
};
Two pt[MAXN+10],s1,s2;
Two fre[MAXN*2+10],hate[MAXN*2+10];
struct Graph
{
vector<int>G[2*MAXN+10],_G[2*MAXN+10];
int ID[MAXN*2+10],ord[MAXN*2+10];
int SCC;
void init(int n)
{
for(int i=1;i<=n;i++)
{
G[i].clear();
_G[i].clear();
}
}
void add(int a,int b)
{
G[a].push_back(b);
_G[b].push_back(a);
}
void dfs1(int u)
{
ID[u]=1;
for(int i=0,size=_G[u].size();i<size;i++)
{
int v=_G[u][i];
if(!ID[v])dfs1(v);
}
ord[++SCC]=u;
}
void dfs2(int u)
{
ID[u]=SCC;
for(int i=0,size=G[u].size();i<size;i++)
{
int v=G[u][i];
if(!ID[v])dfs2(v);
}
}
void kosaraju()
{
memset(ID,0,sizeof(ID));
SCC=0;
for(int i=1;i<=2*N;i++)
{
if(!ID[i])dfs1(i);
}
memset(ID,0,sizeof(ID));
SCC=0;
for(int i=2*N;i;i--)
{
if(!ID[ord[i]])
{
SCC++;
dfs2(ord[i]);//
}
}
}
}graph;
inline int dist(Two &a,Two &b)
{
return abs(a.x-b.x)+abs(a.y-b.y);
}
bool chk(int limit)
{
graph.init(2*N);
//build
for(int i=1;i<N;i++)
for(int j=i+1;j<=N;j++)
{
//dist(i,s1)+dist(j,s1)>L i0->j1 j0->i1
if(dist(pt[i],s1)+dist(pt[j],s1)>limit)
{
graph.add(i,j+N);
graph.add(j,i+N);
}
//dist(i,s2)+dist(j,s2)>L i1->j0 j1->i0
if(dist(pt[i],s2)+dist(pt[j],s2)>limit)
{
graph.add(i+N,j);
graph.add(j+N,i);
}
//dist(i,s1)+dist(s1,s2)+dist(s2,j)>L i0->j0 j1->i1
if(dist(pt[i],s1)+dist(s1,s2)+dist(s2,pt[j])>limit)
{
graph.add(i,j);
graph.add(j+N,i+N);
}
//dist(i,s2)+dist(s2,s1)+dist(s1,j)>L i1->j1 j0->i0
if(dist(pt[i],s2)+dist(s2,s1)+dist(s1,pt[j])>limit)
{
graph.add(i+N,j+N);
graph.add(j,i);
}
}
for(int i=1;i<=A;i++)
{
//i0->j1,i1->j0,j0->i1,j1->i0
graph.add(hate[i].x,hate[i].y+N);
graph.add(hate[i].x+N,hate[i].y);
graph.add(hate[i].y,hate[i].x+N);
graph.add(hate[i].y+N,hate[i].x);
}
for(int i=1;i<=B;i++)
{
//i0->j0,i1->j1,j0->i0,j1->i1
graph.add(fre[i].x,fre[i].y);
graph.add(fre[i].x+N,fre[i].y+N);
graph.add(fre[i].y,fre[i].x);
graph.add(fre[i].y+N,fre[i].x+N);
}
graph.kosaraju();
for(int i=1;i<=N;i++)
{
if(graph.ID[i]==graph.ID[i+N])return false;
}
return true;
}
int main()
{
int Max;
while(~scanf("%d%d%d",&N,&A,&B))
{
scanf("%d%d%d%d",&s1.x,&s1.y,&s2.x,&s2.y);
//Max = 0;
for(int i=1;i<=N;i++)
{
scanf("%d%d",&pt[i].x,&pt[i].y);
//Max = max(Max,dist(pt[i],s1));
//Max = max(Max,dist(pt[i],s2));
}
//Max = 2*Max+dist(s1,s2)+1;
for(int i=1;i<=A;i++)
scanf("%d%d",&hate[i].x,&hate[i].y);
for(int i=1;i<=B;i++)
scanf("%d%d",&fre[i].x,&fre[i].y);
int left = 0,right = INF;
while(right-left>1)
{
int mid = (right+left)>>1;
if(chk(mid))right=mid;
else left=mid;
}
if(right==INF)puts("-1");
else printf("%d\n",right);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<pre class="brush:cpp">/*
POJ 2723 Get Luffy Out
题意:给出N对钥匙,一对钥匙只能用一个 M个门,每个门上2个值,只要有其中一个值的钥匙即可开门了,问最大能开的门,门得按顺序开。
既然是按顺序开,就可以二分枚举能开的门的个数
然后用2-SAT判可行性
建图方法为,对于门上的两个值a,b 显然有_a->b,_b->a
*/
/**//*
POJ 3678 Katu Puzzle
6个逻辑表达式,建图用
a AND b = 1 <==> _a->a _b->b
a AND b = 0 <==> a->_b b->_a
a OR b = 1 <==> _a->b _b->a
a OR b = 0 <==> a->_a b->_b
a XOR b = 1 <==> _a->b a->_b _b->a b->_a
a XOR b = 0 <==> a->b _a->_b b->a _b->_a
其实记住三个就够了,另外三个就两边取反就可以了
*/</pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<pre class="brush:cpp">/*
POJ 3648 Wedding
题意:n对夫妻,夫妻不能坐在同一排。m种通奸关系,有通奸关系的a,b不能同时坐在新娘的对面一排,求方案
以新娘为参考系,x表示坐在新娘一侧,_x表示坐在对面一侧
有通奸关系的a,b 有_a->b _b->a
最后!新娘一定要选上,所以连0h->0w 这样子连,拓扑排序后自底向上肯定会选上新娘
缩点建图后再拓扑排序
select的过程,遇到没有标记的块就选上它,并把它内所有点的对立点及其前代都标记删除
最后删除掉的肯定是n个
这里对反图建图,所以直接拓扑排序就可以自底向上了
*/
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int MAXN = 1000;
struct Graph
{
vector<int>G[MAXN*2+10],_G[MAXN*2+10];
vector<int>Dag[MAXN*2+10],inDag[MAXN*2+10];
int n,SCC;
int ID[MAXN*2+10],ord[MAXN*2+10];
int in[MAXN*2+10],pos[MAXN*2+10];
int color[MAXN*2+10];
void init(int nn)
{
n=nn;
for(int i=0;i<2*n;i++)
{
G[i].clear();
_G[i].clear();
}
}
void add(int u,int v)
{
G[u].push_back(v);
_G[v].push_back(u);
}
void dfs1(int u)
{
ID[u]=1;
for(int i=0,size=_G[u].size();i<size;i++)
{
int v=_G[u][i];
if(ID[v]==-1)dfs1(v);
}
ord[SCC++]=u;
}
void dfs2(int u)
{
ID[u]=SCC;
for(int i=0,size=G[u].size();i<size;i++)
{
int v=G[u][i];
if(ID[v]==-1)dfs2(v);
}
}
void kosaraju()
{
SCC = 0;
memset(ID,-1,sizeof(ID));
for(int i=0;i<2*n;i++)
if(ID[i]==-1)dfs1(i);
SCC = 0;
memset(ID,-1,sizeof(ID));
for(int i=2*n-1;i>=0;i--)
if(ID[ord[i]]==-1)
{
dfs2(ord[i]);
SCC++;
}
}
bool chk()
{
for(int i=0;i<2*n;i+=2)
if(ID[i]==ID[i+1])return false;
return true;
}
void build_Dag()//对反图建DAG 这样topoSort就是自底向上的了
{
for(int i=0;i<SCC;i++)
{
Dag[i].clear();
inDag[i].clear();
}
memset(in,0,sizeof(in));
for(int u=0,v;u<2*n;u++)
{
inDag[ID[u]].push_back(u);
for(int i=0,size=_G[u].size();i<size;i++)
{
v=_G[u][i];
if(ID[u]!=ID[v])
{
Dag[ID[u]].push_back(ID[v]);
++in[ID[v]];
}
}
}
}
void topo_Sort()
{
queue<int>Q;
for(int i=0;i<SCC;i++)
if(in[i]==0)Q.push(i);
int cnt = 0;
while(!Q.empty())
{
int u=Q.front();Q.pop();
pos[cnt++]=u;
for(int i=0,size=Dag[u].size();i<size;i++)
{
int v=Dag[u][i];
if(--in[v]==0)Q.push(v);
}
}
}
void setColor(int u)
{
color[u]=2;
for(int i=0,size=Dag[u].size();i<size;i++)
{
int v=Dag[u][i];
if(color[v]!=2)setColor(v);
}
}
void select()
{
memset(color,0,sizeof(color));
for(int i=0;i<SCC;i++)
{
int u=pos[i];
if(color[u]==0)
{
color[u]=1;//select
for(int j=0,size=inDag[u].size();j<size;j++)//set every _v
{
int v=inDag[u][j]^1;
if(color[ID[v]]==2)continue;
setColor(ID[v]);
}
}
}
}
void solve()
{
build_Dag();
topo_Sort();
select();
for(int i=2;i<2*n;i++)
if(color[ID[i]]==1)printf("%d%c ",i/2,(i&1)?'h':'w');
puts("");
}
}graph;
int main()
{
//freopen("in","r",stdin);
int n,m,a,b;
char ca,cb;
while(scanf("%d%d",&n,&m),n)
{
graph.init(n);
for(int i=0;i<m;i++)
{
scanf("%d%c%d%c",&a,&ca,&b,&cb);
a=a*2+(ca=='h');
b=b*2+(cb=='h');
//_a->b _b->a
graph.add(a^1,b);
graph.add(b^1,a);
}
//0h->0w 0wife一定要选
graph.add(1,0);
graph.kosaraju();
if(!graph.chk())puts("bad luck");
else graph.solve();
}
return 0;
}</pre>
还有2道
POJ 3207 把线当成SAT的点
POJ 3683 跟wedding类似
- 2 – SAT 总结
- 【总结】2-SAT
- 2-sat总结
- 2-sat总结
- 2-sat问题总结
- 2-sat总结
- 2-sat概括性总结
- 2-SAT总结
- 【图论】2-sat总结
- 2-SAT总结
- 2-SAT学习总结
- 2SAT总结
- 2-sat总结
- 【2-SAT】POJ 2-SAT总结
- 【转】2-SAT题目总结
- 2-sat 相关问题总结
- 【研究总结】2-sat问题
- 2sat建边总结
- MFC 实现开机运行程序 以及 运行时隐藏
- Hadoop入门—Linux下伪分布式计算的安装与wordcount的实例展示
- C#使用串口SerialPort开发短信猫收发短信系统总结
- 枚举、递归和二分查找
- MFC 程序最小化到系统托盘区的一个简单实例 .
- 2 – SAT 总结
- Tomcat内存溢出的三种情况及解决办法分析
- hdu 1102 kruskal
- 编译成Release的连接问题
- Spring AOP 详解
- 苹果CEO乔布斯在斯坦福大学的演讲稿
- MessageBox
- 句柄
- MFC 运用CFileFind 类 递归实现文件夹的 复制 (MFC 文件操作 二)