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;
}