2-SAT问题(题目)

2-SAT问题

现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x] AND A[y]=0、A[x] OR A[y] OR A[z]=1等,要确定A[0..N-1]的值,使得其满足所有限制关系。这个称为SAT问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题。

因为如果元素多个,算不出来,这是NP问题

https://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html

洛谷的讲解非常好:https://www.luogu.com.cn/problemnew/solution/P4782

解决方法:强连通分量+拓扑排序

建图:通过限制关系建图

ps. 注意点的个数为2*N,因为x为i,-x为i+n

建好图之后,求出强连通分量SCC,一个SCC内部都是互相依赖的,所以一个SCC以内不能出现夫妻关系(x与-x),如果所有的SCC内部都没有夫妻关系,就有合法的组合

把每个SCC看成是一个点,构成了DAG图,进行反图的拓扑排序,再选中点时排除图中相矛盾的点,就能找到合法的组合。

洛谷的讲解:

 

 最后的输出为col[i]>col[i+n]

P4782 【模板】2-SAT 问题

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=4e6+10;
const int INF=0x3fffffff;
typedef long long LL;
//2-SAT模板问题
//洛谷的讲解非常好:https://www.luogu.com.cn/problemnew/solution/P4782
int col[maxn],head[maxn];
int low[maxn],num[maxn],dfn,ans;
int vis[maxn],sta[maxn],top;
int n,m;
struct node{
	int to,next;
}ed[maxn];
int cnt;
void add(int f,int t){
	ed[++cnt].to=t;
	ed[cnt].next=head[f];
	head[f]=cnt;
}

/*
void create(){
	scanf("%d %d",&n,&m);
	int x,y,a,b;
	for(int i=0;i<m;i++){
		scanf("%d %d %d %d",&x,&a,&y,&b);
		if(a&&b){  // a, b 都真,-a -> b, -b -> a
			g[a+n].push_back(b);
			g[b+n].push_back(a);
		}
		else if(!a&&b){// a 假 b 真,a -> b, -b -> -a
			g[a].push_back(b);
			g[b+n].push_back(a+n);
		}
		else if(a&&!b){  // a 真 b 假,-a -> -b, b -> a
			g[a+n].push_back(n+b);
			g[b].push_back(a);
		}
		else if(!a&&!b){ // a, b 都假,a -> -b, b -> -a
			g[a].push_back(b+n);
			g[b].push_back(a+n);
		}
	}
}  
*/
/*   更简单的方式 
n = read(), m = read();
for (int i = 0; i < m; ++i) {
    int a = read(), va = read(), b = read(), vb = read();
    g[a + n * (va & 1)].push_back(b + n * (vb ^ 1));
    g[b + n * (vb & 1)].push_back(a + n * (va ^ 1));
}
*/

void tarjin(int u,int fa){
	low[u]=num[u]=++dfn;
	sta[++top]=u;
	vis[u]=1;
	for(int i=head[u];i;i=ed[i].next){
		int v=ed[i].to;
		if(v==fa) continue;
		if(!num[v]){
			tarjin(v,u);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v]){
			low[u]=min(low[u],num[v]);
		}
	}
	if(low[u]==num[u]){
		ans++;
		while(sta[top]!=u){
			col[sta[top]]=ans;
			vis[sta[top]]=0;
			top--;
		}
		col[sta[top]]=ans;
		vis[sta[top]]=0;
		top--; //给u 
	}
}
int main(){
	scanf("%d %d",&n,&m);
	int a,b,x,y;
	for(int i=0;i<m;i++){
		scanf("%d %d %d %d",&a,&x,&b,&y);
		if(x==0&&y==0)      //"如果第a个数为0或第b个数为0"至少满足其一 
        {
            add(a+n,b);     //a=1则b=0 
            add(b+n,a);     //b=1则a=0 
        }
        if(x==0&&y==1)      //"如果第a个数为0或第b个数为1"至少满足其一 
        {
            add(a+n,b+n);   //a=1则b=1 
            add(b,a);       //b=0则a=0 
        }
        if(x==1&&y==0)      //"如果第a个数为1或第b个数为0"至少满足其一
        {
            add(a,b);       //a=0则b=0 
            add(b+n,a+n);   //b=1则a=1 
        }
        if(x==1&&y==1)      //"如果第a个数为1或第b个数为1"至少满足其一
        {
            add(a,b+n);     //a=0则b=1 
            add(b,a+n);     //b=0则a=1 
        }
	}
	for(int i=1;i<=(n<<1);i++){
		if(!num[i]) tarjin(i,0);
	}
	//就可以得到答案了,如果两个取值是在同一个SCC中就不可能
	for(int i=1;i<=n;i++){
		if(col[i]==col[i+n]){
			printf("IMPOSSIBLE
");
			return 0;
		}
	} 
	printf("POSSIBLE
");
	for(int i=1;i<=n;i++){
		printf("%d ",col[i]>col[i+n]);  //注意这个输出 
	}
return 0;
}

P4171 [JSOI2010]满汉全席

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=4010;
const int INF=0x3fffffff;
typedef long long LL;
int readd(){
	int f=1,x=0;
	char ss=getchar();
	while(ss<'0'||ss>'9') {
		if(ss=='-') f=-1;
		ss=getchar();
	}
	while(ss>='0'&&ss<='9'){
		x=x*10+ss-'0';
		ss=getchar();
	}
	return f*x;
}
int n,m,k;
int head[maxn],low[maxn],num[maxn],col[maxn],st[maxn],vis[maxn];
int top,ans,cnt,dfn;
struct node{
	int to,next;
}ed[maxn];

void adde(int f,int t){
	ed[++cnt].to=t;
	ed[cnt].next=head[f];
	head[f]=cnt;
}
void tarjin(int u,int fa){
	low[u]=num[u]=++dfn;
	vis[u]=1;
	st[++top]=u;
	for(int i=head[u];i;i=ed[i].next){
		int v=ed[i].to;
		if(!num[v]){
			tarjin(v,u);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v]){
			low[u]=min(low[u],num[v]);
		}
	}
	if(low[u]==num[u]){
		ans++;
		do{
			vis[st[top]]=0;
			col[st[top]]=ans;
			top--;
		}while(u!=st[top]);
	}
}
void inti(){
	memset(head,0,sizeof(head));
	memset(vis,0,sizeof(vis));
	memset(low,0,sizeof(low));
	memset(st,0,sizeof(st));
	memset(num,0,sizeof(num));
	memset(col,0,sizeof(col));
	cnt=0,dfn=0,top=0,ans=0;
}
int main(){
	scanf("%d",&k);
	while(k--){
		inti();
		scanf("%d %d",&n,&m);
		char str1[10],str2[10];
		for(int i=0;i<m;i++){
			scanf("%s %s",str1,str2);
			int a=0,b=0;
			for(int j=1;j<strlen(str1);j++) a=a*10+(str1[j]-'0');
			for(int j=1;j<strlen(str2);j++) b=b*10+(str2[j]-'0');
			if(str1[0]=='m'&&str2[0]=='m'){  //0 0
				adde(a+n,b);  //1 0
				adde(b+n,a);   //1 0
			}
			else if(str1[0]=='m'&&str2[0]=='h'){  //0 1
				adde(a+n,b);  //1 0
				adde(b,a);   //0 0
			}
			else if(str1[0]=='h'&&str2[0]=='m'){ //1 0
				adde(a,b);  //0 0
				adde(b+n,a+n);    //1 1
			}
			else if(str1[0]=='h'&&str2[0]=='h'){  // 1 1
				adde(a,b+n);
				adde(b,a+n) ;  //0 1
			}
		}
		for(int i=1;i<=(n<<1);i++){
			if(!num[i]) tarjin(i,0);
		}
		bool flag=true;
		for(int i=1;i<=n;i++){
			if(col[i]==col[i+n]){
				flag=0;
				break;
			}
		}
		if(flag) printf("GOOD
");
		else printf("BAD
");
	}
return 0;
}

P5782 [POI2001]和平委员会

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=40010;
const int INF=0x3fffffff;
typedef long long LL;
int head[maxn],vis[maxn],low[maxn],num[maxn],col[maxn];
int ans,cnt,top,dfn,n,m;
stack<int> st;
struct node{
	int t,next;
}ed[maxn];
void add(int u,int v){
	ed[++cnt].next=head[u];
	ed[cnt].t=v;
	head[u]=cnt;
}
void tarjin(int u){
	low[u]=num[u]=++dfn;
    st.push(u); 
	vis[u]=1;
    for(int i=head[u];i;i=ed[i].next)
    {
        int v=ed[i].t;
        if(!num[v]){ 
		tarjin(v); 
		low[u]=min(low[u],low[v]);}
        else if(vis[v])
        low[u]=min(low[u],num[v]);
    }
    if(low[u]==num[u])
    {
        int v; 
		ans++;
        do{
            v=st.top();
            st.pop(); 
			vis[v]=0;
            col[v]=ans;
        }
        while(v!=u);
    }
}
int chan(int x){
	return ((x%2)? x+1:x-1);
}
int main(){
	scanf("%d %d",&n,&m);
	int x,y;
	for(int i=0;i<m;i++){
		scanf("%d %d",&x,&y);
		add(x,chan(y));
		add(y,chan(x));
	}
	for(int i=1;i<=(n<<1);i++){
		if(!num[i]) tarjin(i);
	}
	bool flag=1;
	for(int i=1;i<=2*n;i++){   //每个党派有两个代表,所以要乘 2
		if(i%2==1&&col[i]==col[i+1]) {
			flag=0;
			break;
		}
	}
	if(!flag) printf("NIE
");
	else{
		for(int i=1;i<=2*n;i++){
			if(col[i]<col[chan(i)]){
				printf("%d
",i);
			}
		}
	}
return 0;
}

  

原文地址:https://www.cnblogs.com/shirlybaby/p/12550518.html