• 解题报告 APIO 2007 风铃


    No.1风铃  APIO 2007

         风铃是一种多层的装饰品,一般挂在天花板上。每个风铃都包含一些由竖直的线连起来的水平杆。每根杆的两端都有线连接,下面或者挂着另一根水平杆,或者挂着一个玩具。下面是一个风铃的例子:

            

         

    但是maxingc是一个完美主义者(说到完美主义者,还真是恐怖啊,柯南剧场版的前两部凶手都是完美主义者啊),他对风铃的要求如下:

         (1) 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

        (2) 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

     风铃可以按照下面的规则重新排列:任选一根杆,将杆两端的线“交换”。也就是解开一根杆左右两端的线,然后将它们分别绑到杆的另一端。注意这个操作不会改变下面的杆上线的排列顺序。

       Maxingc给窄森一个风铃,让他在1s中之内将它调整到所要求的样子,否则就不让他吃午饭,可怜的窄森怎么会在1s内完成,所以要求助于学oi的你。你只需在一秒钟内说出最少需要几次交换就行了。

       考虑上面的例子,上图中得风铃满足条件1,却不满足条件2。

       但是,我们可以通过下面的步骤把这个风铃变成一个符合要求的。

              

        

       

    Input

      输入的第一行包含一个整数n (1 ≤ n ≤ 100 000),表示风铃中有多少根杆。接下来的n行描述杆的连接信息。这部分的第i行包含两个由空格分隔的整数li和ri,描述杆i的左右两端悬挂的东西。如果挂的是一个玩具,则对应的值为-1,否则为挂在下面的杆的编号。如果杆i下面挂有其它杆,则这些杆的编号将严格大于i。杆1位于风铃的顶部。

    Output

      输出仅包含一个整数。表示最少需要多少次交换能使风铃满足Ike的条件。如果不可能满足,输出-1。

    Sample Input

    2 3

    -1 4

    5 6

    -1 -1

    -1 -1

    -1 -1

    Sample Output 

      2 

     

     

     

    题目简述:

    一根杆有两端,一端只能挂一个玩具或另一根杆,许多根杆和玩具组成一个风铃,你一次操作可以将一个杆的两端对调,并且不会影响下边的部分,求最少的操作次数,是风铃满足以下条件:(1)任意一对玩具到最顶上的杆的距离之差小于等于1;(2)任意一对玩具必满足距离大的在距离小的左边。如果不可能实现,输出-1

     

     

     

    乍一看来,完全无从下手,只有先分析无解的情况。

    首先,如果存在两个玩具深度之差大于1,由于交换操作不会更改玩具的深度,所以这样肯定是无解的。(话说我考试的时候,好像只判断出来了这一种)

    然后,风铃是一个完全二叉树,因此N个节点的完全二叉树只可能有logN层,一旦递归深度超过了logN,那也无解。当然也可以理解为,如果风铃不是倒数第二层往上是满二叉树,那么一定不行。

    然后,第三个无解的条件就是存在一个杆,杆的两侧都有深度差。

     

    从这个例子,我们不难发现本题的玄机:

    某个杆如果左边存在一个玩具,它的深度小于右边的某个玩具,那么这根杆的两端是必须要交换的。因为无论是该杆上边还是下边的操作都不会对它产生影响,而且它的上面是满二叉树,所以上边假设的情况只能通过交换这根杆来解决,所以这次交换是必须的,而且只需一次。

    于是,我们的算法就呼之欲出了。

     

    算法:

    遍历这棵树,统计每根杆之下的最小深度和最大深度,如果出现无解的情况,就直接退出。如果出现要交换的情况,答案加1。遍历完毕,答案就出来了。

     

     

    代码 Leve

    type tree=record

      lc,rc,c,cnt:longint;

      d:boolean;

      end;

    const maxn=100001;

    var i,n,ans,x:longint;

        t:array[-1..maxn] of tree;

     

    procedure swap(var a,b:longint);

    var t:longint;

    begin

      t:=a; a:=b; b:=t;

    end;

     

    procedure ty(k,y:longint);  // k 为根节点, 为深度。

    var r,l:longint;

    begin

      if k=-1 then exit;

      if ans=-1 then exit;

      if y>x then

        begin

          ans:=-1; exit;

        end;

     

      r:=t[k].rc; l:=t[k].lc;

      ty(l,y+1); ty(r,y+1);

      if ans=-1 then exit;

     

      if t[r].cnt>t[l].cnt then

        begin

          swap(t[k].rc,t[k].lc);

          swap(l,r);

          inc(ans);

        end;

     

      if t[l].c-t[r].c>1 then ans:=-1; //深度差 >1

      if (t[l].d=false) and (t[l].d=t[r].d) then ans:=-1;  //杆两侧都有深度差

      if ans=-1 then exit;

     

      t[k].cnt:=t[l].cnt+t[r].cnt;

      t[k].c:=t[l].c+1;

      if t[l].c<>t[r].c then t[k].d:=false else t[k].d:=true;

    end;

    begin

      assign(input,'Mobiles.in');

      assign(output,'Mobiles.out');

      reset(input);

      rewrite(output);

     

      readln(n);

      for i:=1 to n do 

        readln(t[i].lc,t[i].rc);

      t[-1].cnt:=1; 

      t[-1].c:=1; 

      t[-1].d:=true;

      x:=trunc(ln(n)/ln(2))+1;  //最大深度

      ty(1,1);

      writeln(ans);

    close(input);

    close(output);

    end.

  • 相关阅读:
    [转]Linux(Ubuntu)下如何安装JDK
    第一个MICO CORBA demo实录
    解决/usr/bin/ld: cannot find -lssl
    使用adb shell 进入手机修改文件的权限
    解决某些Android Permission denied
    Java 8新特性终极指南
    Win10系统出问题?简单一招即可修复win10!
    运行时数据区
    linux下vi命令大全
    关于java中final关键字与线程安全性
  • 原文地址:https://www.cnblogs.com/SueMiller/p/2234687.html
Copyright © 2020-2023  润新知