2-SAT

2-SAT,简单的说就是给出n个集合,每个集合有两个元素,已知若干个(a,b) ,表示a与b矛盾(其中a与b属于不同的集合)。

然后从每个集合选择一个元素,判断能否一共选n个两两不矛盾的元素。显然可能有多种选择方案,一般题中只需要求出一种即可。 ——OI wiki

图论的东西学起来都比较简单,至少易懂,但其难点就在于图论建模,如何把关系转化成图中的关系就很难,而且也不一定能想到用图论。

先放板子:P4782 【模板】2-SAT 问题

#include<bits/stdc++.h>
using namespace std;
#define N 1145140
#define M 1919810
#define ll long long
#define inf 0x3f3f3f3f
ll n,m,a,b,c,d;
struct xx{
	ll next,to;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y){
	e[++cnt].next=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
ll low[N],dfn[N],vis[N],s[N],top,t_cnt;
ll color[N],c_cnt;
void tarjan(ll u){
	low[u]=dfn[u]=++t_cnt;
	s[++top]=u;
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next){
		ll v=e[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v]) low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u]){
		color[u]=++c_cnt;
		while(s[top]!=u){
			color[s[top]]=c_cnt;
			vis[s[top]]=0;
			top--;
		}
		vis[s[top]]=0;
		top--;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		cin>>a>>b>>c>>d;
		add(a+(!b)*n,c+d*n);
		add(c+(!d)*n,a+b*n);
	}
	for(int i=1;i<=2*n;++i)
		if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;++i)
		if(color[i]==color[i+n]){
			cout<<"IMPOSSIBLE";
			return 0;
		}
	cout<<"POSSIBLE"<<'\n';
	for(int i=1;i<=n;++i)
		if(color[i]<color[i+n]) cout<<"0 ";
		else cout<<"1 ";
	return 0;
}

一般2-SAT需要改的地方也就一个:建图。只要把建图这步弄对,剩下的就和模板差不多了。

方法:先找出0/1关系,然后分类讨论加边。

P4171 [JSOI2010] 满汉全席

建图:

for(int i=1;i<=m;++i){
	cin>>s1>>t1>>s2>>t2;
	//汉式 +n 
	if(s1=='m'){
		if(s2=='h') add(t1+n,t2+n),add(t2,t1); //都汉都满 
		if(s2=='m') add(t1+n,t2),add(t2+n,t1); //一汉一满 
	}
	if(s1=='h'){
		if(s2=='h') add(t1,t2+n),add(t2,t1+n);
		if(s2=='m') add(t1,t2),add(t2+n,t1+n);
	}
}

P5782 [POI2001] 和平委员会

cin>>n>>m; n*=2;
for(int i=1;i<=m;++i){
	cin>>a>>b;
	add(a,b+n);
	add(b,a+n);
}
for(int i=1;i<=n;i+=2){
	add(i,i+1+n),add(i+1+n,i);
	add(i+n,i+1),add(i+1,i+n);
}

 

posted @ 2023-06-23 14:59  和蜀玩  阅读(20)  评论(0)    收藏  举报