zoukankan      html  css  js  c++  java
  • NOI 2015 荷马史诗【BZOJ 4198】k叉Huffman树

    抱歉因为NOIP集训,好长时间没再写题解了。

    NOI 2015也就只有这道题一看就能懂了……

    4198: [Noi2015]荷马史诗

    Time Limit: 10 Sec  Memory Limit: 512 MB
    Submit: 922  Solved: 473
    [Submit][Status][Discuss]

    Description

    追逐影子的人,自己就是影子。 ——荷马

    Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》组成的鸿篇巨制《荷马史诗》实在是太长了,Allison 想通过一种编码方式使得它变得短一些。
    一部《荷马史诗》中有 n 种不同的单词,从 1 到 n 进行编号。其中第 i 种单词出现的总次数为 wi。Allison 想要用 k 进制串 si 来替换第 i 种单词,使得其满足如下要求:
    对于任意的 1≤i,j≤n,i≠j,都有:si 不是 sj 的前缀。
    现在 Allison 想要知道,如何选择 si,才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,Allison 还想知道最长的 si 的最短长度是多少?
    一个字符串被称为 k 进制字符串,当且仅当它的每个字符是 0 到 k−1 之间(包括 0 和 k−1)的整数。
    字符串 Str1 被称为字符串 Str2 的前缀,当且仅当:存在 1≤t≤m,使得 Str1=Str2[1..t]。其中,m 是字符串 Str2 的长度,Str2[1..t] 表示 Str2 的前 t 个字符组成的字符串。

    Input

    输入文件的第 1 行包含 2 个正整数 n,k,中间用单个空格隔开,表示共有 n 种单词,需要使用 k 进制字符串进行替换。

    接下来 n 行,第 i+1 行包含 1 个非负整数 wi,表示第 i 种单词的出现次数。

    Output

    输出文件包括 2 行。

    第 1 行输出 1 个整数,为《荷马史诗》经过重新编码以后的最短长度。
    第 2 行输出 1 个整数,为保证最短总长度的情况下,最长字符串 si 的最短长度。

    Sample Input

    4 2
    1
    1
    2
    2

    Sample Output

    12
    2

    HINT

    用 X(k) 表示 X 是以 k 进制表示的字符串。
    一种最优方案:令 00(2) 替换第 1 种单词,01(2) 替换第 2 种单词,10(2) 替换第 3 种单词,11(2) 替换第 4 种单词。在这种方案下,编码以后的最短长度为:
    1×2+1×2+2×2+2×2=12
    最长字符串 si 的长度为 2。
    一种非最优方案:令 000(2) 替换第 1 种单词,001(2) 替换第 2 种单词,01(2) 替换第 3 种单词,1(2) 替换第 4 种单词。在这种方案下,编码以后的最短长度为:
    1×3+1×3+2×2+2×1=12
    最长字符串 si 的长度为 3。与最优方案相比,文章的长度相同,但是最长字符串的长度更长一些。
    对于所有数据,保证 2≤n≤100000,2≤k≤9。
    选手请注意使用 64 位整数进行输入输出、存储和计算。
     
    这道题要是没学过Huffman还能想出来就超神了……但是Huffman好像是普及组都学过的知识吧。
    一般的Huffman树都是二叉的,其思想是每次取数列中权值最小的两个节点,合并成一个新的节点作为他们的父亲,其权值为两子节点权值之和。具体实现可以使用优先队列维护(就是个堆)
    而这道题设计的k进制数字串就加大了这个题目的难度(虽然实际上也不是很难)
    题目中的“对于任意的 1≤i,j≤n,i≠j,都有:si 不是 sj 的前缀。”这个条件就告诉我们,这道题是个k叉Huffman树。
    又因为题目中要求最长的si的长度最小值,所以我们可以使用二元组(val,dep)添加到优先队列里面去,排列方法就是先按val的值升序,再按dep的值升序。
    考虑到k比较小,每次贪心Huffman树的时候最多会执行k+1次操作,所以这个对时间复杂度O(nlogn)并没有太大的影响。
    有一个点需要注意:每次选择k个权值最小的点的时候容易让最后一次合并的时候的点不足k个。假设最初有n个点,最后有1个点,每次合并删除k个点又放进1个点。那么易得:(n-1)是(k-1)的倍数。如果(n-1)%(k-1)!=0,那么就要再放入(k-1-(n-1)%(k-1))个虚拟点,并且它们的权值为0,它们也参与求最小k个点。 
    所以,可以得到此题的算法: 
    1)处理这n个权值,加入虚拟点,这些点的val值上文已经告诉,dep值为0,ans=0; 
    2)每次取出前k小的点,求它们的val之和sum,求它们的dep的最大值d,那么放入的新点应该是(sum,d+1),把它放入原来的容器里面并要求有序,且ans+=sum(画一棵哈夫曼树,想想求文章长度的过程能这么实现的原理); 
    3)当容器内只有一个点时,输出ans和这个点的dep值。 
     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <cmath>
     6 #include <cstdlib>
     7 #include <queue>
     8 using namespace std;
     9 typedef long long ll;
    10 int n, k, top;
    11 struct _data {
    12     ll w,h;
    13     bool operator < (const _data &x) const {
    14         return this->w != x.w ? this->w > x.w : this->h > x.h;
    15     }
    16 };
    17 priority_queue<_data> q;
    18 int main() {
    19     scanf("%d%d", &n, &k);
    20     for(int i = 1; i <= n; i++) {
    21         ll w; scanf("%lld", &w);
    22         q.push((_data){w, 0});
    23     }
    24     if((n - 1) % (k - 1) != 0)top = k - 1 - (n - 1) % (k - 1);
    25     for(int i = 1; i <= top; i++)
    26         q.push((_data){0, 0});
    27     top += n;
    28     ll ans = 0;
    29     for(; top != 1; top -= k - 1) {
    30         ll w = 0,h = 0;
    31         for(int i = 1; i <= k; i++) {
    32             _data x = q.top();
    33             q.pop();
    34             w += x.w; h = max(h, x.h);
    35         }
    36         ans += w;
    37         q.push((_data){w, h + 1});
    38     }
    39     printf("%lld
    %lld
    ", ans, q.top().h);
    40     return 0;
    41 }

    注意优先队列的顺序定义(重载<运算符部分)!!!

  • 相关阅读:
    Android开发 ViewConfiguration View的配置信息类
    Android 开发 倒计时功能 转载
    Android 开发 关于7.0 FileUriExposedException异常 详解
    Android 开发 实现文本搜索功能
    Android 开发 Activity里获取View的宽度和高度 转载
    Android 开发 存储目录的详解
    Android 开发 Fresco框架点击小图显示全屏大图实现 ZoomableDraweeView
    Android 开发 将window变暗
    Android 开发 DisplayMetrics获取Android设备的屏幕高宽与其他信息
    Android 开发 DP、PX、SP转换详解
  • 原文地址:https://www.cnblogs.com/gangding/p/5964964.html
Copyright © 2011-2022 走看看