JZOJ 1776. 经济编码 (Standard IO)

来源:互联网 发布:疯狂的java讲义百度云 编辑:程序博客网 时间:2024/05/29 16:55

1776. 经济编码 (Standard IO)

Time Limits: 1000 ms Memory Limits: 128000 KB

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

Hint

【数据约定】
  对于50%的数据,1<=n<=2000;
  对于100%的数据,1<=n<=200000。
  所有字符出现次数和等于文章总长度。
【提示】
  建议使用extended类型处理实数运算。

题解

很坑的一题
算法不难,是哈夫曼树,贪心,跟合并果子一样

关键是一定不能用小数来处理,一定要在最后用double(ans)/double(m)来输出

代码

#include<iostream>#include<iostream>#include<cstdio>#define INF 2147483647#define N 400000using namespace std;long long dui[N*2+1],top;void add(long x){   long now;    dui[++top]=x;    for(now=top;dui[now/2]>dui[now]&&now>1;now/=2)        swap(dui[now],dui[now/2]);}long qu(){   long ans=dui[1],now;    bool t=false;    dui[1]=INF;    now=1;    while(!t){        t=true;        if(now*2==top||dui[now*2]<dui[now*2+1]){            if(dui[now]>dui[now*2]){                swap(dui[now],dui[now*2]);                now=now*2;                t=false;            }        }else if(now*2+1<=top)            if(dui[now]>dui[now*2+1]){                swap(dui[now],dui[now*2+1]);                now=now*2+1;                t=false;            }    }    return ans;}int main(){   long n,m,i,q;    long long ans=0;    scanf("%ld%ld",&n,&m);    for(i=1;i<=n;i++){        scanf("%ld",&q);        add(q);    }    for(i=1;i<n;i++){        q=qu()+qu();        ans+=q;        add(q);    }    printf("%.6lf\n",double(ans)/double(m));    return 0;}
原创粉丝点击