- admin's blog
【模板】2-SAT
- @ 2024-2-27 20:20:38
【模板】2-SAT
概念
何为2-SAT?
- 就是有n个位置,每个位置上要么0要么1。不可能同时存在i位置上既有0又有1。
- 有k个限制条件,在k个限制条件中满足 i 为 0/1 或者 j 为0/1。
- 在满足所有的限制条件的情况下是否能取出所有的i的情况。
限制条件
如何去解决限制条件? 如果i为 1 或 j为0。并且要满足这个情况。那么 只要出现了i为0的时候,j就必须为1。推广来说,我们就可以理解为 ~vi 与 vj 有一条边、vi 与 ~vj 也有一条边。
建边
依据限制条件分析出来的结果,我们讲反向的值进行连边,表示:要使得条件成立,那么如果第一个条件不满足的话第二个条件必须满足
edge[ x+n*(vx&1) ].push_back( y+n*(vy^1) );
edge[ y+n*(vy&1) ].push_back( x+n*(vx^1) );
求解
- 缩点 为什么是进行缩点? 因为在有向图中,每一个点都是一个情况,而如果某个情况中出现了环,那表示取了其中任意一个情况后,环中的其余情况都必须取。
- 遍历判断结果是否符合预期 对每个点继续遍历,看这个点的1和0的情况是否在同一环中,如果是,那么就是无解的。
- 环的先后排序输出结果
如果结果符合预期,就可以输出一个可行的解了,这里使用的输出解的方式就是看环的先后,看1和0所在环的标号。
for(int i=1;i<=n;i++) { printf( "%d ",cor[i]<cor[i+n] ); }
注意事项
为了便于建图,i为i号取0,i+n为取真。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+12;
vector<int>edge[N];
int n,m,dfn[N],vis[N],low[N],cor[N],c,cnt,x,y,vx,vy,a,b;
stack<int>s;
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
s.push(x);
vis[x]=1;
for(auto y:edge[x])
{
if( !dfn[y] )
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if( vis[y] ) low[x]=min(low[x],dfn[y]);
}
if( dfn[x]==low[x] )
{
c++;
while( s.top()!=x )
{
int y=s.top();
s.pop();
cor[y]=c;
vis[y]=0;
}
int y=s.top();
s.pop();
cor[y]=c;
vis[y]=0;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>x>>vx>>y>>vy;
edge[ x+n*(vx&1) ].push_back( y+n*(vy^1) );
edge[ y+n*(vy&1) ].push_back( x+n*(vx^1) );
}
for(int i=1;i<=2*n;i++)
{
if( !dfn[i] ) tarjan(i);
}
for(int i=1;i<=n;i++)
{
if( cor[i]==cor[i+n] )
{
printf("IMPOSSIBLE");
return 0;
}
}
printf("POSSIBLE\n");
for(int i=1;i<=n;i++)
{
printf( "%d ",cor[i]<cor[i+n] );
}
}