2-sat问题

 

2-sat问题

分类: 2-sat问题   199人阅读  评论(0)  收藏  举报

       最近做了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

题意:有好多对情侣,不过中间有通奸的,要求妻子对面坐的人没有通奸,或者成对夫妇。

[cpp]  view plain copy
  1. /  
  2. // File Name: poj3648.cpp  
  3. // Author: Eggache  
  4. // mail: aszmq@163.com  
  5. // Created Time: 2013-8-12 15:10:07  
  6. /  
  7. #include <cstdio>  
  8. #include <cstdlib>  
  9. #include <climits>  
  10. #include <cstring>  
  11. #include <cmath>  
  12.   
  13. #include <algorithm>  
  14. #include <iostream>  
  15. #include <stack>  
  16. #include <queue>  
  17. #include <map>  
  18. using namespace std;  
  19. typedef long long ll;  
  20. #define mid(x,y) (x+y)>>1  
  21. #define INF (INT_MAX/10)  
  22. #define SQR(x) ((x)*(x))  
  23. #define rep(i, n) for (int i=0; i<(n); ++i)  
  24. #define repf(i, a, b) for (int i=(a); i<=(b); ++i)  
  25. #define repd(i, a, b) for (int i=(a); i>=(b); --i)  
  26. #define clr(ar,val) memset(ar, val, sizeof(ar))  
  27. #define N 100000  
  28. struct Edge{  
  29.     int v,to;  
  30. }edge[N+10],nedge[N+10];  
  31. int edgeNum,head[N+10];  
  32. void add(int u,int v){  
  33.     edge[edgeNum].v=v;                       //edge[]   edgeNum  
  34.     edge[edgeNum].to=head[u];                //head[]  
  35.     head[u]=edgeNum++;  
  36. }                               //构造原图的邻接表  
  37. int dfn[N+10],low[N+10],id[N+10],ans[N+10],s[N+10];  
  38. int cnt,scnt,beg;  
  39. void dfs(int x){  
  40.     low[x]=dfn[x]=++cnt;                     //low[]   dfn[]   cnt  
  41.     s[++beg]=x;                              //s[]     beg  
  42.     int v;  
  43.     for(int i=head[x];i!=-1;i=edge[i].to){  
  44.         v=edge[i].v;  
  45.         if(!dfn[v]){  
  46.             dfs(v);  
  47.             low[x]=min(low[v],low[x]);  
  48.         }  
  49.         else if(!id[v])                      //id[]     
  50.             low[x]=min(dfn[v],low[x]);  
  51.     }  
  52.     if(dfn[x]==low[x]){  
  53.         int tmp=0;  
  54.         scnt++;                              //scnt  
  55.         do{  
  56.             tmp++;  
  57.             v=s[beg--];  
  58.             id[v]=scnt;  
  59.         }while(v!=x);  
  60.         ans[scnt]=tmp;                       //ans[]  
  61.     }  
  62. }  
  63. void tarjan(int n){  
  64.     cnt=beg=scnt=0;  
  65.     clr(dfn,0);  
  66.     rep(i, n) if(!dfn[i]) dfs(i);  
  67. }                                 //求强连通分量tarjan算法  
  68. int nhead[N+10];  
  69. void nadd(int u,int v){  
  70.     nedge[edgeNum].v=v;                     //nedge[]  
  71.     nedge[edgeNum].to=nhead[u];             //nhead[]  
  72.     nhead[u]=edgeNum++;  
  73. }                                 //构造反向图的邻接表  
  74. int op[N+10],in[N+10],tans[N+10];  
  75. void tdfs(int *in,int x,int n){  
  76.     int v;  
  77.     n++; in[x]=-1;     
  78.     if(n>scnt) return ;  
  79.     for(int i=nhead[x];i!=-1;i=nedge[i].to)  
  80.         v=nedge[i].v,in[v]--;  
  81.     repf(i, 1, scnt){  
  82.         if(in[i]==0){  
  83.             tans[n-1]=i;                   //tans[]  
  84.             tdfs(in, i, n);  
  85.             return ;  
  86.         }  
  87.     }  
  88. }  
  89. void topo(int *in){  
  90.     repf(i, 1, scnt)   
  91.         if(in[i]==0)   
  92.             tans[0]=i,tdfs(in,i,1);  
  93. }                                         //拓扑排序  
  94. int colo[N+10];  
  95. void color(int x){  
  96.     int v;  
  97.     colo[x]=2;  
  98.     for(int i=nhead[x];i!=-1;i=nedge[i].to){  
  99.         v=nedge[i].v;  
  100.         if(!colo[v]) color(v);  
  101.     }  
  102. }                                         //对互斥节点及子孙染色  
  103. int main(){  
  104.     int n,m,mark;  
  105.     int a,b,a0,b0;  
  106.     char c,d;  
  107.     while(1){  
  108.         cin >> n >> m ;  
  109.         if(!n&&!m) break;  
  110.         mark=0;edgeNum=0;  
  111.         clr(head,-1);clr(id,0);  
  112.         rep(i, m){  
  113.             scanf("%d%c%d%c",&a,&c,&b,&d);  
  114.             if(c=='h') a0=a+n;  
  115.             else a0=a,a+=n;  
  116.             if(d=='h') b0=b+n;  
  117.             else b0=b,b+=n;  
  118.             add(a0,b);add(b0,a);  
  119.         }  
  120.         add(0,n);  
  121.         tarjan(2*n);                             //强连通分量搞定  
  122.         //rep(i, 2*n) cout << id[i] << ' ' ;  
  123.         //cout << endl;  
  124.         int v;  
  125.         rep(i, n){  
  126.                 if(id[i+n]==id[i]) mark=1;  
  127.                 op[id[i]]=id[i+n];  
  128.                 op[id[i+n]]=id[i];  
  129.         }                                        //构造互斥点  
  130.         if(mark) {  
  131.             cout << "bad luck" << endl;  
  132.             continue;  
  133.         }  
  134.         edgeNum=0;clr(nhead,-1);  
  135.         clr(in,0);  
  136.         rep(i, n){  
  137.             for(int w=head[i];w!=-1;w=edge[w].to){  
  138.                 v=edge[w].v;  
  139.                 if(id[i]!=id[v]){  
  140.                     nadd(id[v],id[i]);           //边反向连接  
  141.                     in[id[i]]++;  
  142.                 }  
  143.             }  
  144.         }  
  145.         //repf(i, 1, 2*n) cout << in[i] << ' ' ;  
  146.         //cout << endl;  
  147.         topo(in);                                //拓扑排序搞定  
  148.         //rep(i,  scnt) cout << tans[i] << ' ' ;  
  149.         //cout << endl;  
  150.         clr(colo,0);  
  151.         rep(i, scnt){  
  152.             if(!colo[tans[i]]){  
  153.                 colo[tans[i]]=1;  
  154.                 color(op[tans[i]]);  
  155.             }  
  156.         }                                        //染色完毕  
  157.         repf(i, 1, n-1){  
  158.             if(colo[id[i]]==1)  
  159.                 cout << i << 'h' ;  
  160.             else   
  161.                 cout << i << 'w' ;  
  162.             if(i<n-1)  
  163.                 cout << ' ' ;  
  164.             else   
  165.                 cout << endl;  
  166.         }  
  167.     }  
  168.     return 0;  
  169. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值