uva 11294 2-sat

/**
挺简单的一道题wa了好久..应该还是当时没完全理解2-sat模板就随便套..
这个模板中,构造一个有向图G,其中每个变量拆成两个节点2i和2i+1,分别表示xi为假和xi为真,之前是把这两个真假弄反了
对于xi为真或xj为假这样的条件,连有向边2i+1->2j以及2j+1->2i
对于每个没有赋值的变量,设为xi,我们先假定它为假,然后标记节点2i,并沿着有向边标记所有能标记的点,如果过程中发
现某个变量对应的两个节点都被标记,说明这个假设不成立,改成xi为真然后重新标记。

对于这道题,假设某个人为节点xi,xi为假表示他坐在新郎那一侧,xi为真表示他坐在新娘那一侧。
那么本题就给了两个约束条件。首先,夫妻不能同时为真也不能同时为假。其次,吵过架的人不能同时为假。
还有一个条件就是新郎一定为假。因为我们假设的是xi为假表示他坐在新郎那侧。当然也可以换一种方式假设,结果也是一样的。
**/


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=100;

struct TwoSAT
{
   int n;
   vector<int> G[maxn*2];
   bool mark[maxn*2];
   int S[maxn*2],c;

   bool dfs(int x)
   {
      if(mark[x^1]) return false;
      if(mark[x]) return true;
      mark[x]=true;
      S[c++]=x;
      for(int i=0;i<G[x].size();i++)
         if(!dfs(G[x][i])) return false;
      return true;
   }

   void init(int n)
   {
      this->n=n;
      for(int i=0;i<n*2;i++) G[i].clear();
      memset(mark,0,sizeof(mark));
   }

   void add_clause(int x,int xval,int y,int yval)
   {
      x=x*2+xval;
      y=y*2+yval;
      G[x^1].push_back(y);
      G[y^1].push_back(x);
   }

   bool solve()
   {
      for(int i=0;i<n*2;i+=2)
      {
         if(!mark[i] && !mark[i+1])
         {
            c=0;
            if(!dfs(i))
            {
               while(c>0) mark[S[--c]]=false;
               if(!dfs(i+1)) return false;
            }
         }
      }
      return true;
   }
};

TwoSAT solver;

int main()
{
   int a,b,n,m;
   char c[10],d[10];
   while(scanf("%d%d",&n,&m)!=EOF)
   {
      if(n==0 && m==0) break;
      solver.init(n*2);
      solver.mark[0]=true;
      for(int i=0;i<m;i++)
      {
         scanf("%d%s%d%s",&a,c,&b,d);
         if(c[0]=='w')a+=n;
         if(d[0]=='w')b+=n;
         solver.add_clause(a,1,b,1);
      }
      for(int i=0;i<n;i++)
      {
         solver.add_clause(i,0,i+n,0);
         solver.add_clause(i,1,i+n,1);
      }
      if(!solver.solve()) printf("bad luck");
      else
      {
         if(solver.mark[1*2+1]) printf("1h");
         else printf("1w");
         for(int i=2;i<n;i++)
         {
            if(solver.mark[i*2+1]) printf(" %dh",i);
            else printf(" %dw",i);
         }
      }
      printf("\n");
   }
   return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值