最近做了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
题意:有好多对情侣,不过中间有通奸的,要求妻子对面坐的人没有通奸,或者成对夫妇。
-
-
-
-
-
-
- #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].to=head[u];
- 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;
- s[++beg]=x;
- 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])
- low[x]=min(dfn[v],low[x]);
- }
- if(dfn[x]==low[x]){
- int tmp=0;
- scnt++;
- do{
- tmp++;
- v=s[beg--];
- id[v]=scnt;
- }while(v!=x);
- ans[scnt]=tmp;
- }
- }
- void tarjan(int n){
- cnt=beg=scnt=0;
- clr(dfn,0);
- rep(i, n) if(!dfn[i]) dfs(i);
- }
- int nhead[N+10];
- void nadd(int u,int v){
- nedge[edgeNum].v=v;
- nedge[edgeNum].to=nhead[u];
- 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;
- 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);
-
-
- 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]]++;
- }
- }
- }
-
-
- topo(in);
-
-
- 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;
- }