Pku 3678 Katu Puzzle

7 篇文章 0 订阅
6 篇文章 0 订阅

题目:

 Katu Puzzle

来源:

 Pku 3678

题目大意:

 有N个变量,为0或1,给出一堆逻辑关系,问是N个变量是否有一种取值方案。

数据范围:

 N (1 ≤ N ≤ 1000) and M,(0 ≤ M ≤ 1,000,000)

样例:

 4 4
 0 1 1 AND
 1 2 1 OR
 3 2 0 AND
 3 0 0 XOR
YES

做题思路:

 把每个变量X看成两个点,I代表0,I'代表1。
 把每个变量Y看成两个点,J代表0,J'代表1。
 Add(I,J)代表从I向J连一条有向边。
 构图,分情况讨论:
 1) X AND Y=1,Add(I',J'),Add(J',I'),Add(I,I'),Add(J,I')
 2) X AND Y=0,Add(I',J),Add(J',I)
 3) X OR Y=1,Add(I,J'),Add(J,I')
 4) X OR Y=0,Add(I,J),Add(J,I),Add(I',I),Add(J',J)
 5) X XOR Y=1,Add(I',J),Add(J',I),Add(I,J'),Add(J,I')
 6) X XOR Y=0,Add(I',J'),Add(J',I'),Add(I,J),Add(J,I)
 请注意上面的红色部分,由于X AND Y=1 或 X OR Y=0后,X和Y的值已经确定,所以  选择另一个点时必然会指向已确定值的点。

 2-sat,构图很强大。。。

知识点:

2-sat验证、kosaraju

type
 edge=record
 y,next:longint;
 end;
//====================================================
var
 a1,a2:array[0..1000010]of edge;
 first1,first2,q1,f:array[0..2020]of longint;
 time,tot1,tot2,n,m:longint;
//=========================================================
procedure build1(x,y:longint);
begin
 inc(tot1);
 a1[tot1].y:=y;
 a1[tot1].next:=first1[x];
 first1[x]:=tot1;
end;
//==================================================================
procedure build2(x,y:longint);
begin
 inc(tot2);
 a2[tot2].y:=y;
 a2[tot2].next:=first2[x];
 first2[x]:=tot2;
end;
//============================================================
procedure init;
var
 i,a,b,c,j,t:longint;
 s:string;
 ch:char;
begin
 readln(n,m);
 tot1:=0;tot2:=0;
 fillchar(first1,sizeof(first1),0);
 fillchar(first2,sizeof(first2),0);
 for i:=1 to m do
  begin
  readln(a,b,c,ch,s);
  inc(a);inc(b);
   case s[1] of{<我发现kosaraju的缺点了,就是条件多了建图好麻烦>}
   'A':if c=1 then
           begin
            build1(2*a,2*b);build2(2*b,2*a);
            build1(2*a-1,2*a);build2(2*a,2*a-1);
            build1(2*b,2*a);build2(2*a,2*b);
            build1(2*b-1,2*a);build2(2*a,2*b-1);
           end
          else
           begin
            build1(2*a,2*b-1);build2(2*b-1,2*a);
            build1(2*b,2*a-1);build2(2*a-1,2*b);
           end;
   'O':if c=1 then
          begin
            build1(2*a-1,2*b);build2(2*b,2*a-1);
           build1(2*b-1,2*a);build2(2*a,2*b-1);
          end
         else
          begin
           build1(2*a-1,2*b-1);build2(2*b-1,2*a-1);
           build1(2*b-1,2*a-1);build2(2*a-1,2*b-1);
            build1(2*a,2*a-1);build2(2*a-1,2*a);
           build1(2*b,2*b-1);build2(2*b-1,2*b);
          end;
   'X':if c=1 then
           begin
            build1(2*a,2*b-1);build2(2*b-1,2*a);
            build1(2*b,2*a-1);build2(2*a-1,2*b);
            build1(2*a-1,2*b);build2(2*b,2*a-1);
            build1(2*b-1,2*a);build2(2*a,2*b-1);
           end
          else
           begin
            build1(2*a,2*b);build2(2*b,2*a);
            build1(2*b,2*a);build2(2*a,2*b);
            build1(2*a-1,2*b-1);build2(2*b-1,2*a-1);
            build1(2*b-1,2*a-1);build2(2*a-1,2*b-1);
           end;
   end;
  end;
end;
//===========================================================
procedure dfs1(x:longint);
var
 t:longint;
begin
 f[x]:=1;
 t:=first1[x];
 while t>0 do
  begin
   if f[a1[t].y]=0 then dfs1(a1[t].y);
  t:=a1[t].next;
  end;
 inc(time);
 q1[time]:=x;
end;
//==========================================================
procedure dfs2(x:longint);
var
 t:longint;
begin
 f[x]:=time;
 t:=first2[x];
 while t>0 do
  begin
   if f[a2[t].y]=0 then dfs2(a2[t].y);
  t:=a2[t].next;
  end;
end;
//==============================================================
procedure kosaraju;
var
 i:longint;
begin
 time:=0;
 fillchar(f,sizeof(f),0);
 for i:=1 to n*2 do
  if f[i]=0 then dfs1(i);
 time:=0;
 fillchar(f,sizeof(f),0);
 for i:=n*2 downto 1 do
  if f[q1[i]]=0 then
  begin
   inc(time);
   dfs2(q1[i]);
   end;
end;
//=======================================================
procedure main;
var
 i:longint;
begin
 for i:=1 to n do
  if(odd(i))and(f[2*i]=f[2*i-1]) then
  begin
   writeln('NO');
   exit;
   end;
 writeln('YES');
end;
//==========================================================
begin
 init;
 kosaraju;
 main;
end.
题目来源: http://poj.org/problem?id=3678

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值