/**
挺简单的一道题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;
}
uva 11294 2-sat
最新推荐文章于 2019-10-01 10:52:08 发布