poj 3678

Description

Katu Puzzle ispresented as a directed graph G(VE) with eachedge e(a, b) labeled by a boolean operator op (oneof AND, OR, XOR) and an integer c (0 ≤ c ≤1). One Katu is solvable if one can find each vertex Vi avalue Xi (0 ≤ X≤ 1)such that for each edge e(a, b) labeled by op and c,the following formula holds:

 Xa op Xb = c

The calculating rulesare:

AND

0

1

0

0

0

1

0

1

OR

0

1

0

0

1

1

1

1

XOR

0

1

0

0

1

1

1

0

Given a Katu Puzzle,your task is to determine whether it is solvable.

Input

The first linecontains two integers N (1 ≤ N ≤ 1000) and M,(0≤ M ≤ 1,000,000) indicating the number of vertices and edges.
The following M lines contain three integers (0≤ a < N), b(0 ≤ b < N), c andan operator op each, describing the edges.

Output

Output a linecontaining "YES" or "NO".

SampleInput

4 4

0 1 1 AND

1 2 1 OR

3 2 0 AND

3 0 0 XOR

SampleOutput

YES

Hint

X0 = 1, X1 =1, X2 = 0, X3 = 1

 

题目大意:给m组关系(例如:0 1 1 AND,表示在B={b0,b1,……,bn}中,b0 and b1=1),问是否有方案使得所有条件成立。

 

分析:下面我们先把每个变量拆成两个点,一个表示值为true,一个表示值为false。然后定义连边(i,j),表示选了i必须选j。显然最后如果表示i为true和表示i为false的两个点在同一个强连通分量,那么就无解,否则就有解。按照逆拓扑序,逐个确定对应变量的值,最终就可以得到一个可行解。

对于本题,设i表示该变量为false,i’表示该变量为true。

i and j=1-------------->add(i,i')   add(j,j')

i and j=0-------------->add(i',j)   add(j',i)

i or j=1---------------->add(i,j')   add(j',i)

i or j=0---------------->add(i',i)   add(j',j)

i xor j=1--------------->add(i',j)   add(j',i)  add(i,j') add(j,i')

i xor j=0---------------> add(i,j)   add(i',j') add(j,i)  add(j',i')

 

因为只需要求是否有解,所以我用的是tarjan。

 

程序:

const

 maxn=2005;

 maxm=4000005;

type

 node=record

 x,y,next:longint;

end;

 

var

 n,m,e,cnt,d,num,tot:longint;

 ls,dfn,low,belong,stack:array [1..maxn] of longint;

 g:array [1..maxm] of node;

 instack:array [1..maxn] of boolean;

 

procedure add(x,y:longint);

begin

  inc(e);

  g[e].x:=x;

  g[e].y:=y;

  g[e].next:=ls[x];

  ls[x]:=e;

end;

 

procedure init;

var

  i,j,x,y,ans:longint;

  ch:char;

begin

 read(n,m);

 e:=0;

  for i:=1 to m do

   begin

     read(x,y,ans);

      inc(x); inc(y);

      repeat

       read(ch);

     until ch<>' ';

     readln;

     if ch='A' then

       if ans=1 then

         begin

           add(x,x+n);

           add(y,y+n);

         end

       else

         begin

           add(x+n,y);

           add(y+n,x);

          end;

     if ch='O' then

       if ans=1 then

         begin

           add(x,y+n);

           add(y,x+n);

         end

       else

         begin

           add(x+n,x);

           add(y+n,y);

         end;

     if ch='X' then

       if ans=1 then

         begin

           add(x,y+n);

           add(x+n,y);

            add(y,x+n);

            add(y+n,x);

         end

       else

         begin

            add(x,y);

            add(y,x);

            add(x+n,y+n);

            add(y+n,x+n);

         end;

   end;

end;

 

function min(x,y:longint):longint;

 begin

  if x<y then exit(x)

        else exit(y);

 end;

 

procedure tarjan(i:longint);

var j,t:longint;

 begin

 inc(d);

 dfn[i]:=d;

 low[i]:=d;

 inc(tot);

 stack[tot]:=i;

 t:=ls[i];

 instack[i]:=true;

 while t>0 do

  begin

   j:=g[t].y;

   if dfn[j]=0 then

    begin

     tarjan(j);

     low[i]:=min(low[i],low[j]);

    end

    else

     if instack[j] then

      low[i]:=min(low[i],dfn[j]);

   t:=g[t].next;

  end;

  if low[i]=dfn[i] then

  begin

   inc(num);

   repeat

    j:=stack[tot];

    instack[j]:=false;

    dec(tot);

    belong[j]:=num;

   until i=j;

  end;

 end;

 

procedure solve;

 var i:longint;

begin

 d:=0; num:=0;

 fillchar(dfn,sizeof(dfn),0);

 for i:=1 to n*2 do

  if dfn[i]=0 then

  tarjan(i);

end;

 

procedure output;

 var i:longint;

begin

  for i:=1 to n do

   if belong[i]=belong[i+n] then

   begin writeln('NO'); halt; end;

 writeln('YES');

end;

 

begin

 init;

 solve;

 output;

end.

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值