学习笔记第二十八节:2-SAT

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83894493

正题

      我又来划水了。

      2-SAT问题是类似于这样的形式:

      给出n个数\in 0\ or \ 1,m组条件,问你这m组条件是否能同时满足。

      每组条件类似于这样的形式(x=a\ |\ y=b)[a,b\in 0\ or\ 1]

      怎么做?

       我们可以拆点!

       把每个点拆成两个点,一个表示选,一个表示不选。

       那么假如有一个这样一个条件:x=0\ |\ y=0

       考虑x取1时,y必须取0;y取1时,x必须取0.

       所以我们建两条边:(x_1,y_0),(y_1,x_0)

       表示的就是上面的意思。

扫描二维码关注公众号,回复: 4358910 查看本文章

       接下来,让我们透彻理解边的意思。

       边的意思就是,如果a满足,b必须满足。

       显然x_0,x_1不可能同时取。

       在图中怎么表示呢?!!原来是强联通分量!首先,强联通分量的要么同时取要么同时不取。

       所以如果x_0,x_1在同一强联通分量,那么很明显是矛盾的。如果有,输出Impossible。

       否则就肯定存在一组解。a\to b有路径,那么a取,b必须取。

       对于x_0,x_1如果在一个联通分量,那么肯定取拓扑序大的,为什么?因为如果取拓扑序小的,那么拓扑序大的那个可能也会取到,就会矛盾,为了避免意外,我们选择拓扑序大的。

       如果不在一个连通分量,那么我们取那个都无所谓。

       所以,取拓扑序大的就可以了。(当然这里的拓扑序是缩点后的拓扑序

       最后,因为Tarjan是深搜,搜出来的拓扑序一定是倒序的,所以写的时候选小的。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<stack>
using namespace std;

int n,m;
struct edge{
	int y,next;
}s[2000010];
int first[2000010],len=0;
int t=0,T=0;
int dfn[2000010],low[2000010],tim[2000010];
bool tf[2000010];
stack<int> f;

void ins(int x,int y){
	len++;
	s[len]=(edge){y,first[x]};first[x]=len;
}

void Tarjan(int x){
	dfn[x]=low[x]=++t;
	f.push(x);tf[x]=true;
	for(int i=first[x];i!=0;i=s[i].next){
		int y=s[i].y;
		if(!dfn[y]){
			Tarjan(y);
			low[x]=min(low[y],low[x]);
		}
		else if(tf[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		T++;
		int d;
		tim[x]=T;
		tf[x]=false;
		while((d=f.top())!=x){
			f.pop();
			tim[d]=T;
			tf[d]=false;
		}
		f.pop();
	}
}

int main(){
	scanf("%d %d",&n,&m);
	int x,a,y,b;
	for(int i=1;i<=m;i++){
		scanf("%d %d %d %d",&x,&a,&y,&b);
		if(a==0 && b==0) ins(x,y+n),ins(y,x+n);
		if(a==0 && b==1) ins(x,y),ins(y+n,x+n);
		if(a==1 && b==0) ins(x+n,y+n),ins(y,x);
		if(a==1 && b==1) ins(x+n,y),ins(y+n,x);
	}
	for(int i=1;i<=2*n;i++)	
		if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=n;i++)
		if(tim[i]==tim[i+n]){
			printf("IMPOSSIBLE");
			return 0;
		}
	printf("POSSIBLE\n");
	for(int i=1;i<=n;i++)
		printf("%d ",tim[i]<tim[i+n]);
	printf("\n");
}

       

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/83894493
今日推荐