2-sat学习笔记
2-sat
题目:[模板]2-SAT 问题
题目描述:
有n个布尔变量,另有m个需要满足的条件,每个条件的形式都是“为true/false或为true/false”。比如“为真或为假”、“为假或为假”。2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。
题解:
0表示不选,1表示选
1.a=0 , a->a'
2.a=1 , a'->a
3.a=1那么b=1 , a->b b'->a'(隐藏b=0那么a=0)
4.a=0那么b=0 , a'->b' b->a(隐藏b=1那么a=1)
5.a=1那么b=0 , a->b' b->a'(隐藏b=1那么a=0)
6.a=0那么b=1 , a'->b b'->a(隐藏b=0那么a=1)
7.a=0并且b=0 a=0并且b=1 a=1并且b=0 a=1并且b=1 参考3-6,进行合并
8.a^b=0 a^b!=0 参考3-6,进行合并
如果col[a]==col[a'],那么无解。
关于求一组解,由于我们知道tarjan的dfn序其实就是拓扑的反序,所以当col[a]<col[a']时,a=1。
代码:
#include<bits/stdc++.h> using namespace std; #define next Next #define last Last /*char buf[1<<21],*p1=buf,*p2=buf; inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}*/ #define gc getchar inline int read() { int ret=0,f=0;char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){ret=ret*10+c-48;c=gc();} if(f)return -ret;return ret; } const int N=2e6+5; int n,m,top,deep,tot,color; int dfn[N],low[N],zhan[N],col[N],head[N]; bool vis[N]; struct node{ int too,next; }edge[N]; void add(int a,int b) { edge[++top].too=b;edge[top].next=head[a];head[a]=top; } void tarjan(int u) { deep++;dfn[u]=low[u]=deep; vis[u]=true;zhan[++tot]=u; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].too; if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);} else if(vis[v])low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { color++;vis[u]=false;col[u]=color; while(zhan[tot]!=u) { vis[zhan[tot]]=false; col[zhan[tot]]=color; tot--; } tot--; } } bool TWO_SAT() { for(int i=1;i<=2*n;i++) if(!dfn[i])tarjan(i); for(int i=1;i<=n;i++) if(col[i]==col[i+n])return false; return true; } signed main() { n=read();m=read(); for(int i=1;i<=m;i++) { int a=read(),x=read(),b=read(),y=read(); if(x==0) { if(y==0)//a=1那么b=0(b=1那么a=0) { add(a,b+n); add(b,a+n); } else{//a=1那么b=1(b=0那么a=0) add(a,b); add(b+n,a+n); } } else{ if(y==0)//a=0那么b=0(b=1那么a=1) { add(a+n,b+n); add(b,a); } else{//a=0那么b=1(b=0那么a=1) add(a+n,b); add(b+n,a); } } } if(TWO_SAT()) { puts("POSSIBLE"); for(int i=1;i<=n;i++) printf("%d ",col[i]<col[i+n]); } else printf("IMPOSSIBLE"); return 0; }
题目:[JSOI2010]满汉全席
代码:
#include<bits/stdc++.h> using namespace std; const int N=100005; int T,n,m,x,y,xx,yy,deep,color,cnt,tot,top; int dfn[N],low[N],head[N],col[N],zhan[N]; char s1[50],s2[50]; struct node{ int too,next; }edge[N]; bool vis[N]; void add(int a,int b) { edge[++top].too=b;edge[top].next=head[a];head[a]=top; } void tarjan(int u) { deep++; dfn[u]=low[u]=deep; zhan[++tot]=u;vis[u]=true; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].too; if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);} else if(vis[v])low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { color++;col[u]=color;vis[u]=false; while(zhan[tot]!=u) { col[zhan[tot]]=color; vis[zhan[tot]]=false; tot--; } tot--; } } bool TWO_SAT() { for(int i=1;i<=2*n;i++) if(!dfn[i])tarjan(i); for(int i=1;i<=n;i++) if(col[i]==col[i+n])return false; return true; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); cnt=tot=0; for(int i=1;i<=2*n;i++) head[i]=low[i]=dfn[i]=col[i]=vis[i]=0; for(int i=1;i<=m;++i) { scanf("%s%s",s1,s2); int u=0,v=0,k; k=1;while(s1[k]>='0'&&s1[k]<='9')u=u*10+s1[k++]-'0'; k=1;while(s2[k]>='0'&&s2[k]<='9')v=v*10+s2[k++]-'0'; if(s1[0]=='m') { if(s2[0]=='h')add(u+n,v+n),add(v,u); else if(s2[0]=='m')add(u+n,v),add(v+n,u); } else if(s1[0]=='h') { if(s2[0]=='h')add(u,v+n),add(v,u+n); else if(s2[0]=='m')add(u,v),add(v+n,u+n); } } if(TWO_SAT())printf("GOOD\n"); else printf("BAD\n"); } }
题目:[NOI2017]游戏
题解:
a,b,c三种赛道都是普通2-set。但x赛道不是。
怎么办?由于x赛道数量少,我们暴力枚举是变成a或b即可。
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int n,m,num,deep,color,tot,top; int dfn[N],low[N],head[N],col[N],zhan[N],b[N],pos[N]; char s[N]; struct node{ int too,next; }edge[N]; struct node2{ int x,a,y,b; }a[N]; bool vis[N]; /*char buf[1<<21],*p1=buf,*p2=buf; inline int gc() { return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++; }*/ #define gc getchar inline int read() { int ret=0,f=0; char c=gc(); while(!isdigit(c)) { if(c=='-')f=1; c=gc(); } while(isdigit(c)) { ret=ret*10+c-48; c=gc(); } if(f)return -ret; return ret; } int get(int x,int c) { if(b[x]==1)return c==2?x:x+n; if(b[x]==2||b[x]==3)return c==1?x:x+n; } int nxt(int x) { return x>n?x-n:x+n; } void clean() { top=color=deep=tot=0; memset(head,0,sizeof(head)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); memset(col,0,sizeof(col)); } void add(int a,int b) { edge[++top].too=b;edge[top].next=head[a];head[a]=top; } void build() { for(int i=1;i<=m;i++) { int x=a[i].x,y=a[i].y; if(b[x]==a[i].a)continue; int u=get(x,a[i].a),v=get(y,a[i].b); if(b[y]==a[i].b) { add(u,nxt(u)); continue; } add(u,v);//选a,一定要选b add(nxt(v),nxt(u));//不选b,一定不选a } } void tarjan(int u) { deep++; dfn[u]=low[u]=deep; zhan[++tot]=u;vis[u]=true; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].too; if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);} else if(vis[v])low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { color++;col[u]=color;vis[u]=false; while(zhan[tot]!=u) { col[zhan[tot]]=color; vis[zhan[tot]]=false; tot--; } tot--; } } bool TWO_SAT() { for(int i=1;i<=2*n;i++) if(!dfn[i])tarjan(i); for(int i=1;i<=n;i++) if(col[i]==col[i+n])return false; return true; } void dfs(int x) { if(x>num) { clean(); build(); if(TWO_SAT()) { for(int i=1;i<=n;i++) { if(col[i]<col[nxt(i)])putchar(b[i]==1?'B':'A'); else putchar(b[i]==3?'B':'C'); } exit(0); } return; } b[pos[x]]=1; dfs(x+1); b[pos[x]]=2; dfs(x+1); } int main() { n=read();read(); cin>>s+1; for(int i=1;i<=n;i++) { if(s[i]=='x')pos[++num]=i; b[i]=s[i]-'a'+1; } m=read(); char c1,c2; for(int i=1;i<=m;i++) { cin>>a[i].x>>c1>>a[i].y>>c2; a[i].a=c1-'A'+1; a[i].b=c2-'A'+1; } dfs(1); puts("-1"); return 0; }
题目:National Property
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int n,m,deep,color,tot,top,a[N],b[N]; int dfn[N],low[N],head[N],col[N],zhan[N]; struct node{ int too,next; }edge[N]; bool vis[N]; void add(int a,int b) { edge[++top].too=b;edge[top].next=head[a];head[a]=top; } void tarjan(int u) { deep++; dfn[u]=low[u]=deep; zhan[++tot]=u;vis[u]=true; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].too; if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);} else if(vis[v])low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { color++;col[u]=color;vis[u]=false; while(zhan[tot]!=u) { col[zhan[tot]]=color; vis[zhan[tot]]=false; tot--; } tot--; } } bool TWO_SAT() { for(int i=1;i<=2*n;i++) if(!dfn[i])tarjan(i); for(int i=1;i<=n;i++) if(col[i]==col[i+n])return false; return true; } int main() { scanf("%d%d",&m,&n); for(int j=1;j<=m;j++) { scanf("%d",&a[0]); for(int i=1;i<=a[0];i++)scanf("%d",&a[i]); if(j>1) { for(int i=1;i<=min(a[0],b[0]);i++) { if(b[i]<a[i]) { add(a[i],b[i]); add(b[i]+n,a[i]+n); break; } else if(a[i]<b[i]) { add(b[i]+n,b[i]); add(a[i],a[i]+n); break; } if(i==min(a[0],b[0])&&b[0]>a[0]) { puts("No"); return 0; } } } for(int i=0;i<=a[0];i++)b[i]=a[i]; } if(TWO_SAT()) { puts("Yes"); int ans=0; for(int i=1;i<=n;i++) if(col[i]<col[i+n])ans++; cout<<ans<<endl; for(int i=1;i<=n;i++) if(col[i]<col[i+n])printf("%d ",i); } else printf("No"); return 0; }
xuxuxuxuxu 文章被收录于专栏
信息学竞赛