2—sat问题

 在实际问题中,2-SAT问题在大多数时候表现成以下形式:有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。


在程序实现中,我们把初始的n个物品变成2n个节点,然后从0开始编号到2*n-1号。其中原始第i个物品对应节点i*2和i*2+1。如果我们mark[i*2]节点,那么表示我们i节点设为假,如果我们mark[i*2+1]节点,那么我们i节点设为真。同一个节点只能mark一种结果(即对于原始i来说,我们只能mark[i*2]mark[i*2+1]其中之一)。


然后加入存在i假或j假的论述,我们就引一条图中从2*i+1到2*j的边,再引一条2*j+1到2*i的边,表示如果i是真的,那么j肯定是假的(否则之前的结论不成立)。且如果j是真的,那么i肯定是假的(否则之前的结论也不成立)。


算法模板:

  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<vector>  
  4. using namespace std;  
  5. const int maxn=10000+10;  
  6. struct TwoSAT  
  7. {  
  8.     int n;  
  9.     vector<int> G[maxn*2];  
  10.     bool mark[maxn*2];  
  11.     int S[maxn*2],c;  
  12.   
  13.     bool dfs(int x)  
  14.     {  
  15.         if(mark[x]) return true;  
  16.         if(mark[x^1]) return false;  
  17.         mark[x]=true;  
  18.         S[c++]=x;  
  19.         for(int i=0;i<G[x].size();i++)  
  20.             if(!dfs(G[x][i])) return false;  
  21.         return true;  
  22.     }  
  23.   
  24.     void init(int n)  
  25.     {  
  26.         this->n=n;  
  27.         for(int i=0;i<2*n;i++) G[i].clear();  
  28.         memset(mark,0,sizeof(mark));  
  29.     }  
  30.   
  31.     void add_clause(int x,int xval,int y,int yval)  
  32.     {  
  33.         x=x*2+xval;  
  34.         y=y*2+yval;  
  35.         G[x^1].push_back(y);  
  36.         G[y^1].push_back(x);  
  37.     }  
  38.   
  39.     bool solve()  
  40.     {  
  41.         for(int i=0;i<2*n;i+=2)  
  42.         if(!mark[i] && !mark[i+1])  
  43.         {  
  44.             c=0;  
  45.             if(!dfs(i))  
  46.             {  
  47.                 while(c>0) mark[S[--c]]=false;  
  48.                 if(!dfs(i+1)) return false;  
  49.             }  
  50.         }  
  51.         return true;  
  52.     }  
  53. };  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值