2-sat入门(例题hdu1814,poj3648)持续更新
来源:互联网 发布:学cnc编程 编辑:程序博客网 时间:2024/05/16 16:13
2-sat问题描述
2-sat问题是这样的:有n个布尔变量xi,另有m个需要满足的条件,每个条件的形式都是“xi为真/假或者xj为真/假”。比如“x1为真或者x2为假”。注意这里的“或”是指两个条件至少一个是正确的。
由于在2-SAT问题中,最多只对两个元素进行限制,所以可能的限制关系共有11种:
A[x] (1)
NOT A[x] (2)
A[x] AND A[y]
A[x] AND NOT A[y]
A[x] OR A[y] (3)
A[x] OR NOT A[y] (4)
NOT (A[x] AND A[y]) (5)
NOT (A[x] OR A[y]) (6)
A[x] XOR A[y] (7)
NOT (A[x] XOR A[y]) (8)
A[x] XOR NOT A[y] (9)
进一步,A[x] AND A[y]相当于(A[x]) AND (A[y])(也就是可以拆分成A[x]与A[y]两个限制关系),NOT(A[x] OR A[y])相当于NOT A[x] AND NOT A[y](也就是可以拆分成NOT A[x]与NOT A[y]两个限制关系)。因此,可能的限制关系最多只有9种。
比如A[x]or B[x],那么如果!A[x]就必然有B[x],所以从!A[x]连一条有向边连向B[x],同理如果!B[x]就必然有A[x],所以从!B[x]连一条有向边连向A[x]。这里的有向边可以理解为推导出。
对于单个的A[x],就相当于A[x]||A[x],从!A[x]连向A[x]即可。
于是可以构造有向图G,G包含2n个顶点,代表2n个元素。则将问题转化为从G中选出n个顶点,并且ai和(~ai)的顶点不能同时被选,使其满足2-sat的条件。
染色法
逐一考虑每个没有赋值的变量,设为xi。我们先假定它是假的,然后标记结点2i,并且沿着有向边标记所有能标记的结点。如果标记过程中发现某个变量对应的两个结点都被标记,则“xi为假”这个假定不成立,需要改为“xi为真”,然后重新标记。注意,这个算法没有回溯过程。如果当前考虑的变量不管赋值是真还是假都会引起矛盾,可以证明整个2-sat问题无解
虽然这个算法复杂度为O(n*m),但是它的一个好处就是可以求字典序
hdu1814
Peaceful Commission
#include <iostream>#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <stack>#include <vector>#include <string.h>#include <queue>#define msc(X) memset(X,-1,sizeof(X))#define ms(X) memset(X,0,sizeof(X))typedef long long LL;using namespace std;const int MAXN=20020;const int MAXM=100010;struct _Edge{ int to,next;}edge[MAXM];int hd[MAXN],tot;void inti(void){ tot=0; msc(hd);}void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=hd[u]; hd[u]=tot++;}bool vs[MAXN];//染色标记,true表示选择int Stack[MAXN],top;bool dfs(int u){ if(vs[u^1]) return false; if(vs[u]) return true; vs[u]=true; Stack[top++]=u; for(int i=hd[u];i!=-1;i=edge[i].next) if(!dfs(edge[i].to)) return false; return true;}bool twosat(int n){ ms(vs); for(int i=0;i<n;i+=2) { if(vs[i]||vs[i^1]) continue;//可选择 top=0; if(!dfs(i)){ while(top) vs[Stack[--top]]=false; if(!dfs(i^1)) return false;//如果i和i^1都不能选,无解 } } return true;}int main(int argc, char const *argv[]){ int n,m; while(scanf("%d %d",&n,&m)==2){ inti(); while(m--){ int a,b; scanf("%d %d",&a,&b); a--,b--; addedge(a,b^1); addedge(b,a^1); } if(twosat(2*n)){ for(int i=0;i<2*n;i++) if(vs[i]) printf("%d\n",i+1 ); } else puts("NIE"); } return 0;}
强连通+拓扑排序
图建好,可以求出G的强连通分量,如果选中强连通分量的一个点,那么就必须选中强连通分量的所有点。如果ai和~ai在一个强连通分量中,则ai和~ai就必须都选中,矛盾,故此时2-sat问题无解。 如果没有产生矛盾,就将强连通分量进行缩点,缩点后重新建反向图G*。由于 G *中没有环,所以它有拓扑结构。按拓扑顺序将点进行染色。
算法复杂度O(m),但不能求字典序,只能给出一个任意解。
poj3648
Wedding
#include <iostream>#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <stack>#include <vector>#include <string.h>#include <queue>#define msc(X) memset(X,-1,sizeof(X))#define ms(X) memset(X,0,sizeof(X))typedef long long LL;using namespace std;const int MAXN=1010;const int MAXM=100010;struct _Edge{ int to,next;}edge[MAXM];int hd[MAXN],tot;void inti(void){ tot=0; msc(hd);}void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=hd[u]; hd[u]=tot++;}int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];int Index,top;int scc;bool Instack[MAXN];void tarjan(int u){ int v; Low[u]=DFN[u]=++Index; Stack[top++]=u; Instack[u]=true; for(int i=hd[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(!DFN[v]){ tarjan(v); if(Low[u]>Low[v]) Low[u]=Low[v]; } else if(Instack[v]&&Low[u]>DFN[v]) Low[u]=DFN[v]; } if(Low[u]==DFN[u]){ scc++; do{ v=Stack[--top]; Instack[v]=false; Belong[v]=scc; }while(v!=u); }}bool solvable(int n)//n是总个数,需要选择一半{ ms(DFN); ms(Instack); Index=scc=top=0; for(int i=0;i<n;i++) if(!DFN[i]) tarjan(i); for(int i=0;i<n;i+=2) if(Belong[i]==Belong[i^1]) return false; return true;}queue<int > q;vector<vector<int > >dag;//缩点后的逆图char color[MAXN];int indeg[MAXN];int cf[MAXN];void solve(int n){ dag.assign(scc+1,vector<int> ()); ms(indeg); ms(color); for(int u=0;u<n;u++) for(int i=hd[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(Belong[u]!=Belong[v]) { dag[Belong[v]].push_back(Belong[u]);//反向边 indeg[Belong[u]]++; } } for(int u=0;u<n;u+=2) { cf[Belong[u]]=Belong[u^1]; cf[Belong[u^1]]=Belong[u]; } while(!q.empty()) q.pop(); for(int i=1;i<=scc;i++) if(indeg[i]==0) q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); if(color[u]==0){ color[u]='R'; color[cf[u]]='B'; } int sz=dag[u].size(); for(int i=0;i<sz;i++) { indeg[dag[u][i]]--; if(indeg[dag[u][i]]==0) q.push(dag[u][i]); } }}int change(char *s){ int num=0,i=0; while('0'<=s[i]&&s[i]<='9') num=num*10+s[i++]-'0'; if(s[i]=='w') return num<<1; return num<<1|1;}int main(int argc, char const *argv[]){ int n,m; while(scanf("%d %d",&n,&m)==2&&(n||m)){ inti(); while(m--){ char ss[4],ss2[4]; scanf("%s%s",ss,ss2); int u=change(ss),v=change(ss2); addedge(u^1,v); addedge(v^1,u); } addedge(1,0);//0号一定去,因为求的是和新娘坐在一边的 if(solvable(2*n)){ solve(2*n); for(int i=1;i<n;i++) { if(color[Belong[i<<1]]=='R') printf("%dw",i ); else printf("%dh",i ); printf("%c",i==n-1?'\n':' ' ); } } else puts("bad luck"); } return 0;}
- 2-sat入门(例题hdu1814,poj3648)持续更新
- 2-sat入门hdu1814
- poj3648,2-sat求解
- POJ3648 Wedding 【2-SAT】
- poj3648 Wedding 2-sat
- [HDU1814]Peaceful Commission(2-SAT)
- hdu1814 Peaceful Commission(2-sat)
- hdu1814 Peaceful Commission,2-sat
- hdu1814 Peaceful Commission 2-sat
- hdu1814 Peaceful Commission(2-sat)
- hdu1814 Peaceful Commission 2-SAT
- 2-SAT——7.0(poj3648 Wedding,完结篇)
- Javascript例题(持续更新)
- POJ3648-2SAT解的求得
- poj3648 2-sat <输出任意一组解>
- [2-sat]构造一个可行解(poj3648)
- [2-SAT][POJ2683][HDU1814]2-SAT两种模板
- poj3648:Wedding——题解(配2-SAT简易讲解)
- 为虚拟化扩展“服务器新衣裳”
- GitHub从入门到精通常用命令
- 详解BluetoothAdapter
- git 学习笔记
- linux下nutch的安装配置
- 2-sat入门(例题hdu1814,poj3648)持续更新
- android 多尺寸生产xml
- 工具及其对商业社会的影响思考
- 仿今日头条/商城商品详情指示器
- C++拷贝构造函数详解
- ios打包ipa的四种实用方法(.app转.ipa)
- android lk解读 (2)
- 如何将iphone系统由正式版转为beta测试版
- 一步一步教你如何导出JAR包后将多个JAR包合并,并混淆