• 解题报告 信号放大器


     

    1.        题目   信号放大器

       【问题描述】

     

           树型网络是最节省材料的网络。所谓树型网络,是指一个无环的连通网络,网络中任意两个结点间有且仅有一条通信道路。

           网络中有一个结点是服务器,负责将信号直接或间接地发送到各终端机。如图6-4server结点发出一个信号给结点aca再转发给b。如此,整个网络都收到这个信号了。

    server         a                 b

    ●────○────○

           但是,实际操作中,信号从一个结点发到另一个结点,会出现信号强度的衰减。衰减量一般由线路长度决定。                                          server  3    a     2         b

    ●────○────○

    1

           如上图,边上所标的数字为边的衰减量。假设从server出发一个强度为4个单位的信号,发到结点a后强度衰减为4-3=1个单位。结点a再将其转发给结点b。由于信号强度为1,衰减量为2,因此信号无法发送到b

           一个解决这一问题的方法是,安装信号放大器。信号放大器的作用是将强度大于零的信号还原成初始强度(从服务器出发时的强度)。

           上图中,若在结点a处安装一个信号放大器,则强度为4的信号发到a处,即被放大至4。这样,信号就可以被发送的网络中的任意一个节点了。为了简化问题,我们假定每个结点只处理一次信号,当它第二次收到某个信号时,就忽略此信号。

           你的任务是根据给出的树型网络,计算出最少需要安装的信号放大器数量。

    【输入】

           第一行一个整数n,表示网络中结点的数量。(n<=100000

           2~n+1行,每行描述一个节点的连接关系。其中第i+1行,描述的是结点i的连接关系:首先一个整数k,表示与结点i相连的结点的数量。此后2k个数,每两个描述一个与结点i相连的结点,分别表示结点的编号(编号在1~n之间)和该结点与结点i之间的边的信号衰减量。结点1表示服务器。

           最后一行,一个整数,表示从服务器上出发信号的强度。

    【输出】

           一个整数,表示要使信号能够传遍整个网络,需要安装的最少的信号放大器数量。

           如果不论如何安装信号放大器,都无法使信号传遍整个网络,则输出“No solution.

    【样例】

           booster.in                                  booster.out

           4                                        1

           2 2 3 3 1

           2 1 3 4 2

           1 1 1

           1 2 2

           4

     

    2.        题目实质

    从一个点中正向发送信号使其可以传送到任何一个点且代价最小,不能传送就输出特殊标记。

    3.        算法

    首先,当有某个节点不与任何节点连通(貌似这种情况在数据中不存在)时,以及有某两个节点之间的传输线路的代价值大于最大代价值时,输出特殊标记。

    这道题此处主要讲两种算法:

    我们可以从叶结点往上推,设叶结点收到的信号大小为1(表示至少需要强弱为1的信号),如下图:

    1

    1

    3

    1

    最大信号响度为5

    接下来,往上观察结点a,它有一个子结点b和一个子结点d,两个子结点均需要保证有1的强度,而路上线路长21,所以从结点a法相两个子结点的响度保证值应该为2+1=31+1=2,取其中较大的一个,所以结点a应该返回“至少需要保证强弱为3”。

    最后分析道server,两条线路保证值分别为1+1=23+3=6,由于6>最大信号强度,所以在a的位置必须安装一个扩大器。

    综上所述,也就是在每个结点,将它的子结点综合,求出最大的响度,如果其中有一个响度大于最大响度,那么必须在那个结点的位置安装放大器。

    这个贪心算法为什么起作用呢?为什么在结点a处安装,而不在a的子结点处如b安装呢?很明显,由于信号只能从根往叶传递,那么假设这时可以在abd处安装,那么取他们中辈分最大的一定最优,因为同样满足那一条支(就是图中的a-b那一条),而辈分较大的可以供更多的结点使用。比如说上图,如果响度为4,那么就不能在b安装了。

    这样贪心算法基本建立。

     

    再者,这个题也可以直接搜索。从根节点开始依次往下传输,记录途中加的信号放大器的数量,然后依次遍历每一条路径,记录它的最小值。

     

    但是,鉴于这次考查的是树方面的知识,而且这里对于树的建立也有人问到所以再讲一下如何建立起这棵树。

    首先,由题意得,信号最早是由一号节点发出的,所以这里不妨设一号节点为根节点,然后依次根据读入的连接情况建立起这棵树注意这里的数据中所描述的连接情况中,把这个节点和他的父亲节点的连接情况也囊括了进去,所以本人的算法是用父亲儿子表示法,每次读入一种新的连接情况时,都判断一下这个节点是不是他的父亲节点,这样构建起来的树是多叉树可以转一下二叉树,也可以就拿多叉树算。

    但是,又一位大牛的方法很大牛,因为 1 号节点是根节点,然后依次往下插入他的子节点所以一个点的子节点一定比他本身的编号要大,直接判断新读入的关系中的节点是否比该节点要大即可,省去了调用一次链表的麻烦。

    4.        注意事项

    此处的树的构建比较需要注意链表用不用模拟都可以。

    当然,如果干脆用图的邻接表存自然也米有问题,因为树不过是一种特殊的图。

    5.        时空复杂度  

    搜索 ???

    贪心 On)???

    6.        程序代码

    链表 + 搜索

    type

      pointer=^node;

      node=record

        data,weight:longint;

        next:pointer;

        useful:boolean;

      end;

      arr=array[0..1] of longint;

    var

      ge:array[1..100000] of pointer;

      visited:array[1..100000] of boolean;

      s,n,ans:longint;

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

      begin

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

      end;

    procedure make_tree(i:longint);

      var

        p:pointer;

      begin

        visited[i]:=true;

        p:=ge[i];

        while p<>nil do

          begin

            if not visited[p^.data] then

              make_tree(p^.data)

            else

              p^.useful:=false;

              p:=p^.next;

          end;

      end;

    function cal(i,m:longint):arr;

      var

        p:pointer;

        tmp,re:arr;

      begin

        p:=ge[i];

        re[0]:=0;

        re[1]:=1;

        while p<>nil do

          begin

            if p^.useful then

              begin

                if (m-p^.weight>=1) and (re[0]<maxlongint) then

                  begin

                    tmp:=cal(p^.data,m-p^.weight);

                    re[0]:=re[0]+min(tmp[0],tmp[1]);

                  end

                else

                  re[0]:=maxlongint;

                tmp:=cal(p^.data,s-p^.weight);

                re[1]:=re[1]+min(tmp[0],tmp[1]);

              end;

            p:=p^.next;

          end;

        cal:=re;

      end;

    procedure init;

      var

        i,j,v,w,k:longint;

        p:pointer;

      begin

    //    assign(input,'booster.in');

        reset(input);

        readln(n);

        for i:=1 to n do

          ge[i]:=nil;

        for i:=1 to n do

          begin

            read(k);

            for j:=1 to k do

              begin

                read(v,w);

                new(p);

                p^.data:=v;

                p^.weight:=w;

                p^.useful:=true;

                p^.next:=ge[i];

                ge[i]:=p;

              end;

          end;

        readln(s);

        close(input);

        end;

    procedure work;

      var

        p: pointer;

        tmp: arr;

      begin

        fillchar(visited,sizeof(visited),false);

        make_tree(1);

        p:=ge[1];

        ans:=0;

        while p<>nil do

          begin

            if p^.useful then

              begin

                tmp:=cal(p^.data,s-p^.weight);

                ans:=ans+min(tmp[0],tmp[1]);

              end;

            p:=p^.next;

          end;

      end;

    procedure print;

      begin

    //    assign(output,'booster.out');

        rewrite(output);

        writeln(ans);

        close(output);

      end;

    begin

      init;

      work;

      print;

    end.

     

     

    模拟链表 + 搜索

    type

         stu = record

                  fa, n, len: longint;

                  son: array[1..1000] of longint;

               end;

    var

        f, f1: text;

        n, k, i, j, sum, min: longint;

        a: array[1..100000] of stu;

        s: array[1..100000] of integer;

    procedure init;

    var x, y: longint;

    begin

    //    assign(f,'booster.in');

        reset(f);

    //    assign(f1,'booster.out');

        rewrite(f1);

        readln(f,n);

        for i:=1 to n do

          begin

            read(f,k);

            a[i].n:=0;

            for j:=1 to k do

              begin

                  read(f,x,y);

                  if x>i then

                     begin

                       inc(a[i].n);

                       a[i].son[a[i].n]:=x;

                       a[x].fa:=i;

                       a[x].len:=y;

                     end;

              end;

          end;

        readln(f,min);

        fillchar(s,sizeof(s),0);

        sum:=0;

    end;

    procedure clo;

    begin

        writeln(f1,sum);

        close(f);

        close(f1);

    end;

    procedure try(i: integer);

    var j: integer;

    begin

        if a[i].n=0 then

           s[i]:=0

        else

          begin

             for j:=1 to a[i].n do

               begin

                  try(a[i].son[j]);

                  if s[a[i].son[j]]+a[a[i].son[j]].len>s[i] then

                     s[i]:=s[a[i].son[j]]+a[a[i].son[j]].len;

               end;

             if s[i]>min then

               begin

                  inc(sum);

                  s[i]:=1;

               end;

          end;

    end;

    begin  {main}

       init;

       try(1);

       clo;

    end.

     

     

    链表 + 贪心

    Const

          Inf        = 'Booster.in';

          Ouf        = 'Booster.out';

          MaxN       = 100000;

     Type

         TEg         = Record

                             Fi,Long : Longint;

                       End;

         TList       = Array [1..MaxN] Of TEg;

         TMap        = Array [1..MaxN] Of Record                  {动态空间邻接表来储存边}

                             Num     : Longint;

                             List    : ^TList;

                       End;

         THash       = Array [1..MaxN] Of Boolean;

     Var

        n,m,s        : Longint;

        Map          : TMap;

      Procedure Init;

       Var

          Fin        : Text;

          i,j        : Longint;

       Begin

    //        Assign(Fin,Inf);

            Reset(Fin);

     

            Readln(Fin,n);

            For i:=1 to n Do

                Begin

                     Read(Fin,Map[i].Num);

                     Getmem(Map[i].List,Sizeof(TEg)*Map[i].Num);    {动态分配邻接表的空间}

                     For j:=1 to Map[i].Num Do

                         Read(Fin,Map[i].List^[j].Fi,Map[i].List^[j].Long);

                     Readln(Fin);

                End;

            Readln(Fin,m);

     

            Close(Fin);

       End;

      Procedure Doit;

       Var

          Fou        : Text;

          Hash       : THash;

          i,j,

          Ans        : Longint;

        Procedure NoAnswer;

         Begin

              Writeln(Fou,'No solution.');

              Close(Fou);

              Halt;

         End;

        Function Search(o:Longint):Longint;                            {返回结点o的响度保证值}

         Var

            F        : Boolean;

            i,j,

            Max,Min  : Longint;

         Begin

              F:=True;

              For i:=1 to Map[o].Num Do

                  If Not Hash[Map[o].List^[i].Fi] Then Begin

                     F:=False;

                     Break;

                  End;

              If F Then Exit(1);                               {如果是叶结点}

              Hash[o]:=True; Max:=-1;

    {          Min:=m+1;}

              For i:=1 to Map[o].Num Do                            {处理它的所有子结点}

                  If Not Hash[Map[o].List^[i].Fi] Then Begin

                     j:=Search(Map[o].List^[i].Fi);

                     If j+Map[o].List^[i].Long>m Then Begin        {必须安装扩大器}

                        j:=1;

                        Inc(Ans);

                     End;

                     If j+Map[o].List^[i].Long>Max Then

                        Max:=j+Map[o].List^[i].Long;

                  End;

              Search:=Max;

         End;

       Begin

    //        Assign(Fou,Ouf);

            Rewrite(Fou);

     

            For i:=1 to n Do                              {预先将无解情况排除}

                For j:=1 to Map[i].Num Do

                    If Map[i].List^[j].Long>=m Then NoAnswer;

     

            Fillchar(Hash,Sizeof(Hash),False);

            Hash[1]:=True;

            Ans:=0;

            Search(1);

            Writeln(Fou,Ans);

            Close(Fou);

       End;

     Begin

          Init;

          Doit;

     End.

     

     

    模拟链表 + 贪心 (话说就这个代码短,汗)

    type

      point=record

              data,father,need:integer;

              put:boolean;

            end;

    var

      a:array[1..100000] of point;

      n,i,x,j,p,q,max:integer;

      tot:integer;

    begin

      readln(n);

      for i:=1 to n do

        begin

          read(x);

          a[i].need:=0;

          a[i].put:=false;

          for j:=1 to x do

            begin

              read(p,q);

              if p>i then

                begin

                  a[p].father:=i;

                  a[p].data:=q;

                end;

            end;

        end;

      readln(max);

      for i:=n downto 2 do

        begin

          x:=a[i].need+a[i].data;

          if x>max then

            begin

              if a[x].put=false then tot:=tot+1;

              a[x].put:=true;

              x:=a[i].data;

            end;

          writeln(x);

          if x>a[a[i].father].need then a[a[i].father].need:=x;

        end;

      writeln(tot);

    end.

     

    这个题的解题报告网上相当少见,所以我来写一份。

     

     

  • 相关阅读:
    SharedPreferences
    短信发送器的实现
    第四周总结
    本周开发工作时间及内容
    自我总结
    随笔
    结对编程
    目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点?
    八皇后
    数制转换
  • 原文地址:https://www.cnblogs.com/SueMiller/p/2110551.html
Copyright © 2020-2023  润新知