解题报告 poj 3207

1.        题目

POJ 3207

2.        题目实质

平面上,一个圆,圆的边上按顺时针放着n个点。现在要连m条边,比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接。问能不能连接这m条边,使这些边都不相交。(比如两条边分别是1-2,2-3,则可以连接。若有三条边分别时1-5,2-6,3-7则一定会出现相交)。

3.        算法

2-SAT。(NOI)

题意可能刚开始不是很好理解,比如1 5连边,2,6连边,由于点是顺序排列的,一画图就可以发现,这两条边必须一个从圆外面连,一个从内部连,否则就会相交。如果再加入3 7这条边,那么就必须相交了。

这样,就可以转化成标准的2-sta问题:

1:每个边看成2个点:分别表示在内部连接和在外部连接,只能选择一个。计作点i和点i'

2:如果两条边i和j必须一个画在内部,一个画在外部(一个简单判断就可以)

那么连边:

i->j’, 表示i画内部的话,j只能画外部,即j’

j->i’,同理

i’->j,同理

j’->i,同理

然后就是2-sat算法了,tarjan一下,如果有i和i'同属于一个强联通,返回false,否则就成立。

4.        注意事项

对于 NOIP 的孩子们来说, tarjan 素超纲滴。

5.        代码

2-sat (ZSZ)

program fot;

var belong,l,r:array[0..500010] of longint;

    instack:array[0..500010] of boolean;

    low,dfn,stack,f:array[0..500010] of longint;

    e:Array[0..500010] of record

      y,n:longint;

    end;

    o,i,n,top,j,m,time,number,q,z:longint;

    boo:boolean;

function min(a,b:longint):longint;

begin

  if a<b then exit(a) else exit(b);

end;

procedure swap(var a,b:longint);

var c:longint;

begin

  c:=a;a:=b;b:=c;

end;

procedure add(a,b:longint);

begin

  inc(o);

  e[o].y:=b;

  e[o].n:=f[a];

  f[a]:=o;

end;

procedure tarjan(x:longint);

var t:longint;

begin

  inc(time);

  dfn[x]:=time;

  low[x]:=time;

  inc(top);

  instack[x]:=true;

  stack[top]:=x;

  t:=f[x];

  while t<>0 do

    begin

      if (dfn[e[t].y]=0) then

        begin

          tarjan(e[t].y);

          low[x]:=min(low[x],low[e[t].y]);

        end

      else

      if instack[e[t].y]=true then

        low[x]:=min(low[x],dfn[e[t].y]);

      t:=e[t].n;

    end;

  if dfn[x]=low[x] then

    begin

      inc(number);

      repeat

        t:=stack[top];

        dec(top);

        instack[t]:=false;

        belong[t]:=number;

      until t=x;

    end;

end;

begin

  readln(n,m);

  for i:= 1 to m do

    begin

      readln(l[i],r[i]);

      if (r[i]<l[i]) then swap(r[i],l[i]);

    end;

  o:=0;

  fillchar(e,sizeof(e),0);

  fillchar(f,sizeof(f),0);

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

  fillchar(stack,sizeof(stack),0);

  fillchar(low,sizeof(low),0);

  fillchar(belong,sizeof(belong),0);

  fillchar(instack,sizeof(instack),false);

  for i:= 1 to m do

    for j:= i+1 to m do

      begin

        if ((l[i]>l[j])and(l[i]<r[j])and(r[i]>r[j]))or

        ((l[j]>l[i])and(l[j]<r[i])and(r[j]>r[i])) then

          begin

            add(i,j+m);

            add(j+m,i);

            add(j,i+m);

            add(i+m,j);

          end;

      end;

  top:=0;

  for i:= 1 to 2*m do

    if dfn[i]=0 then tarjan(i);

  boo:=true;

  for i:= 1 to m do

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

      begin

        boo:=false;

        break;

      end;

  if boo=true then writeln('panda is telling the truth...')

  else writeln('the evil panda is lying again');

end.

 

转载于:https://www.cnblogs.com/SueMiller/archive/2011/10/17/2215523.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值