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关系,然后分类讨论加边。
建图:
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);
}
}
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);
}
頑張って

浙公网安备 33010602011771号