• 2017.08.19【NOIP提高组】模拟赛B组 经济编码


    ####Description
    为降低资料储存的空间或增加资料传送的速度,编码是常用的方法。
      假设有一个字符集,每个字符出现的频率是已知的。现在要把每个字符编码成为一个二元字串(例如把“A”编码作101),采用的编码必须合乎以下条件:一个字符的编码不可以是另一个字符的前置(prefix)。前置的定义如下:若一个字串S1为另一个字串S2的前置,则从S2的最后一个字符开始,连续删除一定数量的字符后可以得到S1(S2本身也是S2的前置),举例而言:如果字符“A”的编码是101,而字符“B”的编码为01,则“B”的编码不为“A”编码的前置;如果字符“C”的编码为1100,而字符“D”的是11,则“D”的编码是“C”编码的前置。以下的编码方式可以在符合这个条件下给出最经济的编码,请找出使用下述方法做最经济编码时,一个字符编码的预期长度。
      编码法:
      1、 如以下所述建立一棵二元树。
      先从字符集选取两个出现频率最低的字符作合并,合并后以一个全新的虚拟字符取代这两个字符,新字符的频率等于这两个旧字符频率的总和,并令这两个旧字符为此新字符的两个子树,左右不限。重复以上操作,直至字符集剩下一个字符为止。如下图(i)到(iv)。
      2、 再依照以下所述方法将各字符作编码。
      由上一步骤所得之二元树,将每个内部节点(internal node)连往左子树的边(edge)标记为“0”,连往右子树的边标记为“1”,如下图(V)所示。一字元的编码即为从树根(root)至此字符,经过的每一个边的标记所成之字串。在此“a”编码作000,“?”编码作01。
      在按照上述的编码法完成最经济编码之后,就可以计算这个字符编码的预期长度。首先算出每个字符的预期长度=编码长度×出现频率,然后把所有字符的预期长度结合起来,就可以得到此字符编码的预期长度。下表是上述编码的计算范例。

    这里写图片描述
    这里写图片描述
    字符 编码 编码长度 出现频率 预期长度
    a 000/ 3 0.1 0.3
    b 001/ 3 0.1 0.3
    ? 01/ 2 0.3 0.6
    8 1/ 1 0.5 0.5

    字符编码的预期长度 1.7
    ####Input

    第一行为两整数n和m,分别代表字符集的大小和文章总长度。然后每一个字符分行列出,每行列出一字符出现的次数。

    ####Output

    预期一个字符编码的长度,保留至小数点后6位。

    ####Sample Input

    输入范例1:
    4 10
    1
    1
    3
    5

    输入范例2:
    6 100
    30
    30
    5
    25
    5
    5

    ####Sample Output

    输出范例1:
    1.700000

    输出范例2:
    2.250000
    ####Data Constraint
      对于50%的数据,1<=n<=2000;
      对于100%的数据,1<=n<=200000。
      所有字符出现次数和等于文章总长度。
    ####Hint
      建议使用extended类型处理实数运算。

    题解
    这题题目化简之后就是给出一串数,每次把最小的两个合并并加入答案,求最后只剩一个数时的答案。
    显然合并果子。用堆来做。
    那么只需要维护一个小根堆,每次取出最小和次小,加在一起,加入答案之后放回堆中。

    Code:

    var
            i,j,k,l,n,m,tot:longint;
            answer,x,y:extended;
            a:array[1..400000] of longint;
            gl,d:array[1..400000] of extended;
    
    procedure up(x:longint);
    var
            t:extended;
    begin
            while (x>1)and(d[x]<d[x div 2]) do
            begin
                    t:=d[x];
    	        d[x]:=d[x div 2];
    	        d[x div 2]:=t;
                    x:=x div 2;
            end;
    end;
    
    procedure down(x:longint);
    var
            y:int64;
            t:extended;
    begin
            while (2*x<=tot)and(d[x]>d[2*x]) or(2*x+1<=tot)and(d[x]>=d[2*x+1]) do
            begin
                    y:=2*x;
                    if (y+1<=tot)and(d[y+1]<d[y]) then inc(y);
                    t:=d[x];
                    d[x]:=d[y];
                    d[y]:=t;
                    x:=y;
            end;
    end;
    
    begin
            readln(n,m);
            for i:=1 to n do
            begin
                    readln(a[i]);
                    gl[i]:=a[i]/m;
            end;
            for i:=1 to n do
            begin
                    inc(tot);
                    d[tot]:=gl[i];
                    up(tot);
            end;
            while tot>1 do
            begin
                    x:=d[1];
                    d[1]:=d[tot];
                    dec(tot);
                    down(1);
                    y:=d[1];
                    d[1]:=d[tot];
                    dec(tot);
                    down(1);
                    answer:=answer+x+y;
                    inc(tot);
                    d[tot]:=x+y;
                    up(tot);
            end;
            writeln(answer:0:6);
    end.
    
    我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
  • 相关阅读:
    Java 中String、StringBuffer、StringBuilder的差别
    [转载]StringBuffer versus String
    二维数组的连续子数组的最大和
    一位数组的最大和
    js中常见的去重方式
    魅族2016Java互联网方向其中一道笔试题--青蛙跳台阶问题
    美团在线编程2016--最大差值
    [转载]MySQL开发中常用的查询语句总结
    实现字符串全排列
    笔试题---最大子序列和
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11148420.html
Copyright © 2020-2023  润新知