最近做了2-sat问题,基本没明白2-sat是个什么玩意,不过解题思路明确了,至于为什么……依旧是混沌迷茫中……
网上公认的两篇大牛的文章,在此膜拜……
赵爽 2-sat解法浅析
伍昱 由对称性解2-SAT问题
2-sat问题:我个人理解就是A有两个互斥的选择A和~A,A同时又和其他一些时间不能同时发生,然后在所给时间中找一个可行的方案,即解。
解2-sat问题的方法两篇大牛的文章里已经有完整的证明,我也不会……
一、建图
根据题意找出题目中出现或隐含的互斥关系,然后根据不同时存在的条件构图。即找出原题中的A和~A都代表着什么,A和哪些节点不同时存在。
假设A不与B同时存在,可B总要在B与~B中选择一个作为结果,所以A一定与~B在一个解中,将A和~B连接。当所有的边都找到并存好后,建图阶段
以完成。(PS:是建一条边还是两条边呢?)
二、强连通分量
求出图中的强连通分量(哎,我对名词不敏感,不知道什么是强连通分量,百度后个人认为是一种类似环的东东)。一个强连通分量上的点都是
在一个解里面的,所以如果A与~A都在一个圈里(不到专有名词是什么,这里是A能到~A,~A也能到A,是回路)。这种情况是无解,不用继续算了。
若有解,就把原来的图缩点,成为一个有向无环图(没有圈了)。再进一步求解。
三、拓扑排序
用拓扑排序求出缩点后图的逆图的节点顺序(可能说的不对,反正我就是把原来的图拓扑排序,也可以把边都反向了在拓扑排序)。拓扑的过程
很简单,做一个节点入度的数组,对数组进行遍历。每次拿出入度为零的点,删掉这个点所有的边(就是把到达的节点入度都减一)。
四、染色
按照拓扑排序的顺序进行染色(缩点的图就是拓扑的逆序,逆图就是顺序),每次找一个没有颜色的节点染成红色,并把它的互斥节点、及其子
节点染成蓝色。
五、输出答案
根据染色结果选择答案进行输出。
PS:个人思路,个人想法,可能不对……自己检查……
..............................................................................................................................华丽的分割线..........................................................................................................................
例题 :poj 3648
题意:有好多对情侣,不过中间有通奸的,要求妻子对面坐的人没有通奸,或者成对夫妇。
///////////////////////////////////////////////////////////////////////// // File Name: poj3648.cpp // Author: Eggache // mail: [email protected] // Created Time: 2013-8-12 15:10:07 ///////////////////////////////////////////////////////////////////////// #include <cstdio> #include <cstdlib> #include <climits> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <stack> #include <queue> #include <map> using namespace std; typedef long long ll; #define mid(x,y) (x+y)>>1 #define INF (INT_MAX/10) #define SQR(x) ((x)*(x)) #define rep(i, n) for (int i=0; i<(n); ++i) #define repf(i, a, b) for (int i=(a); i<=(b); ++i) #define repd(i, a, b) for (int i=(a); i>=(b); --i) #define clr(ar,val) memset(ar, val, sizeof(ar)) #define N 100000 struct Edge{ int v,to; }edge[N+10],nedge[N+10]; int edgeNum,head[N+10]; void add(int u,int v){ edge[edgeNum].v=v; //edge[] edgeNum edge[edgeNum].to=head[u]; //head[] head[u]=edgeNum++; } //构造原图的邻接表 int dfn[N+10],low[N+10],id[N+10],ans[N+10],s[N+10]; int cnt,scnt,beg; void dfs(int x){ low[x]=dfn[x]=++cnt; //low[] dfn[] cnt s[++beg]=x; //s[] beg int v; for(int i=head[x];i!=-1;i=edge[i].to){ v=edge[i].v; if(!dfn[v]){ dfs(v); low[x]=min(low[v],low[x]); } else if(!id[v]) //id[] low[x]=min(dfn[v],low[x]); } if(dfn[x]==low[x]){ int tmp=0; scnt++; //scnt do{ tmp++; v=s[beg--]; id[v]=scnt; }while(v!=x); ans[scnt]=tmp; //ans[] } } void tarjan(int n){ cnt=beg=scnt=0; clr(dfn,0); rep(i, n) if(!dfn[i]) dfs(i); } //求强连通分量tarjan算法 int nhead[N+10]; void nadd(int u,int v){ nedge[edgeNum].v=v; //nedge[] nedge[edgeNum].to=nhead[u]; //nhead[] nhead[u]=edgeNum++; } //构造反向图的邻接表 int op[N+10],in[N+10],tans[N+10]; void tdfs(int *in,int x,int n){ int v; n++; in[x]=-1; if(n>scnt) return ; for(int i=nhead[x];i!=-1;i=nedge[i].to) v=nedge[i].v,in[v]--; repf(i, 1, scnt){ if(in[i]==0){ tans[n-1]=i; //tans[] tdfs(in, i, n); return ; } } } void topo(int *in){ repf(i, 1, scnt) if(in[i]==0) tans[0]=i,tdfs(in,i,1); } //拓扑排序 int colo[N+10]; void color(int x){ int v; colo[x]=2; for(int i=nhead[x];i!=-1;i=nedge[i].to){ v=nedge[i].v; if(!colo[v]) color(v); } } //对互斥节点及子孙染色 int main(){ int n,m,mark; int a,b,a0,b0; char c,d; while(1){ cin >> n >> m ; if(!n&&!m) break; mark=0;edgeNum=0; clr(head,-1);clr(id,0); rep(i, m){ scanf("%d%c%d%c",&a,&c,&b,&d); if(c=='h') a0=a+n; else a0=a,a+=n; if(d=='h') b0=b+n; else b0=b,b+=n; add(a0,b);add(b0,a); } add(0,n); tarjan(2*n); //强连通分量搞定 //rep(i, 2*n) cout << id[i] << ' ' ; //cout << endl; int v; rep(i, n){ if(id[i+n]==id[i]) mark=1; op[id[i]]=id[i+n]; op[id[i+n]]=id[i]; } //构造互斥点 if(mark) { cout << "bad luck" << endl; continue; } edgeNum=0;clr(nhead,-1); clr(in,0); rep(i, n){ for(int w=head[i];w!=-1;w=edge[w].to){ v=edge[w].v; if(id[i]!=id[v]){ nadd(id[v],id[i]); //边反向连接 in[id[i]]++; } } } //repf(i, 1, 2*n) cout << in[i] << ' ' ; //cout << endl; topo(in); //拓扑排序搞定 //rep(i, scnt) cout << tans[i] << ' ' ; //cout << endl; clr(colo,0); rep(i, scnt){ if(!colo[tans[i]]){ colo[tans[i]]=1; color(op[tans[i]]); } } //染色完毕 repf(i, 1, n-1){ if(colo[id[i]]==1) cout << i << 'h' ; else cout << i << 'w' ; if(i<n-1) cout << ' ' ; else cout << endl; } } return 0; }