zoukankan      html  css  js  c++  java
  • 【NOI2015】荷马史诗[Huffman树+贪心]

    #130. 【NOI2015】荷马史诗

     统计

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

    Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》组成的鸿篇巨制《荷马史诗》实在是太长了,Allison 想通过一种编码方式使得它变得短一些。

    一部《荷马史诗》中有 nn 种不同的单词,从 11  nn 进行编号。其中第 ii 种单词出现的总次数为 wiwi。Allison 想要用 kk 进制串 sisi 来替换第 ii 种单词,使得其满足如下要求:

    对于任意的 1i,jn1≤i,j≤niji≠j,都有:sisi 不是 sjsj 的前缀。

    现在 Allison 想要知道,如何选择 sisi,才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,Allison 还想知道最长的 sisi 的最短长度是多少?

    一个字符串被称为 kk 进制字符串,当且仅当它的每个字符是 00  k1k−1 之间(包括 00  k1k−1)的整数。

    字符串 Str1Str1 被称为字符串 Str2Str2 的前缀,当且仅当:存在 1tm1≤t≤m,使得 Str1=Str2[1..t]Str1=Str2[1..t]。其中,mm 是字符串 Str2Str2 的长度,Str2[1..t]Str2[1..t] 表示 Str2Str2 的前 tt 个字符组成的字符串。

    输入格式

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

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

    输出格式

    输出文件包括 2 行。

     11 行输出 11 个整数,为《荷马史诗》经过重新编码以后的最短长度。

     22 行输出 11 个整数,为保证最短总长度的情况下,最长字符串 sisi 的最短长度。

    样例一

    input

    4 2
    1
    1
    2
    2
    
    

    output

    12
    2
    
    

    explanation

     X(k)X(k) 表示 XX 是以 kk 进制表示的字符串。

    一种最优方案:令 00(2)00(2) 替换第 11 种单词,01(2)01(2) 替换第 22 种单词,10(2)10(2) 替换第 33 种单词,11(2)11(2) 替换第 44 种单词。在这种方案下,编码以后的最短长度为:

    1×2+1×2+2×2+2×2=121×2+1×2+2×2+2×2=12

    最长字符串 sisi 的长度为 22

    一种非最优方案:令 000(2)000(2) 替换第 11 种单词,001(2)001(2) 替换第 22 种单词,01(2)01(2) 替换第 3 种单词,1(2)1(2) 替换第 44 种单词。在这种方案下,编码以后的最短长度为:

    1×3+1×3+2×2+2×1=121×3+1×3+2×2+2×1=12

    最长字符串 sisi 的长度为 33。与最优方案相比,文章的长度相同,但是最长字符串的长度更长一些。

    样例二

    input

    6 3
    1
    1
    3
    3
    9
    9
    
    

    output

    36
    3
    
    

    explanation

    一种最优方案:令 000(3)000(3) 替换第 11 种单词,001(3)001(3) 替换第 22 种单词,01(3)01(3) 替换第 33 种单词,02(3)02(3) 替换第 44 种单词,1(3)1(3) 替换第 55 种单词,2(3)2(3) 替换第 66 种单词。

    样例三

    见样例数据下载。

    限制与约定

    测试点编号 nn 的规模 kk 的规模备注约定
    1 n=3n=3 k=2k=2   0<wi10110<wi≤1011
    2 n=5n=5 k=2k=2  
    3 n=16n=16 k=2k=2 所有 wiwi 均相等
    4 n=1000n=1000 k=2k=2 wiwi 在取值范围内均匀随机
    5 n=1000n=1000 k=2k=2  
    6 n=100000n=100000 k=2k=2  
    7 n=100000n=100000 k=2k=2 所有 wiwi 均相等
    8 n=100000n=100000 k=2k=2  
    9 n=7n=7 k=3k=3  
    10 n=16n=16 k=3k=3 所有 wiwi 均相等
    11 n=1001n=1001 k=3k=3 所有 wiwi 均相等
    12 n=99999n=99999 k=4k=4 所有 wiwi 均相等
    13 n=100000n=100000 k=4k=4  
    14 n=100000n=100000 k=4k=4  
    15 n=1000n=1000 k=5k=5  
    16 n=100000n=100000 k=7k=7 wiwi 在取值范围内均匀随机
    17 n=100000n=100000 k=7k=7  
    18 n=100000n=100000 k=8k=8 wiwi 在取值范围内均匀随机
    19 n=100000n=100000 k=9k=9  
    20 n=100000n=100000 k=9k=9  

    对于所有数据,保证 2n1000002≤n≤1000002k92≤k≤9

    选手请注意使用 64 位整数进行输入输出、存储和计算。

    时间限制:1s1s

    空间限制:512MB512MB

    评分方式

    对于每个测试点:

    若输出文件的第 11 行正确,得到该测试点 40% 的分数;

    若输出文件完全正确,得到该测试点 100% 的分数。

    下载

    样例数据下载

     

    正解:Huffman+贪心 考这里

    题目要求的限制条件很多,既要求替换后无二义性,又要求方案的最值,还有k进制的限制= =

    Point[哈夫曼树or哈夫曼编码]参考这里

    哈夫曼树一般是二叉树,建树的方法就是每次选择两个权值(即出现次数)最小的点,删除这2个点,加入一个权值是这两个点之和的新点进去。并且使这被删除的2个点的父亲成为那个新点。

    编码的时候左支和右支一个是1一个是0,从根节点到叶子节点经过的边的1/0序列就是叶子节点对应的编码。

    [2叉——>k]

    然而这个题是k叉树,方法和上面类似,然而每次选择k个权值最小的点的时候容易让最后一次合并的时候的点不足k个。假设最初有n个点,最后有1个点,每次合并删除k个点又放进1个点。那么易得:(n-1)是(k-1)的倍数。如果(n-1%k-1)!=0,那么就要再放入(k-1-n-1%k-1))个虚拟点,并且它们的权值为0,它们也参与求最小k个点。

    然而此题还要求si的最大值最小,因此我们让点代表一个二元组(valdep),表示这个点的权值和点在树中的深度。在求最小k个点时,把val作为第一比较条件,如果val值相等,则把dep小的放在前面,这样在每次合并的时候,深度小的点都会被优先合并,保证了根到叶子的最长链的长度尽量小。

    [具体实现]

    1)处理这n个权值,加入虚拟点,这些点的val值上文已经告诉,dep值为0ans=0

    2)每次取出前k小的点,求它们的val之和sum,求它们的dep的最大值d,那么放入的新点应该是(sumd+1),把它放入原来的容器(堆or优先队列)里面并要求有序,且ans+=sum(画一棵哈夫曼树,想想求文章长度的过程能这么实现的原理);

    3)当容器内只有一个点时,输出ans和这个点的dep值。

    时间复杂度:Onlogkn

     

     

    #include<cstdio>
    #include<queue>
    #include<iostream>
    typedef long long ll;
    using namespace std;
    inline void read(ll &x){
        register char ch=getchar();x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    }
    struct node{
        ll val,dep;
        node(){}
        node(ll _val,ll _dep){
            val=_val;dep=_dep;
        }
        bool operator <(const node &a)const{
            return val!=a.val?val>a.val:dep>a.dep;
        }
    }t;
    priority_queue<node>q;
    ll n,m,k,w,ans,DEEP;
    int main(){
        read(n);read(k);
        for(int i=1;i<=n;i++) read(w),q.push(node(w,1));
        if((w=(n-1)%(k-1))){
            w=k-1-w;n+=w;
            for(int i=1;i<=w;i++) q.push(node(0,1));
        } 
        for(ll tot,maxd;n>1;n-=k-1){
            tot=0;maxd=0;
            for(int i=k;i;i--){
                t=q.top();q.pop();
                tot+=t.val;
                maxd=max(maxd,t.dep);
            }
            q.push(node(tot,maxd+1));
            ans+=tot;
            DEEP=max(DEEP,maxd);
        }
        cout<<ans<<'
    '<<DEEP;
        return 0;
    }
  • 相关阅读:
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 3037 Saving Beans【Lucas定理】【模板题】【模板】【组合数取余】
    8.Math 对象
  • 原文地址:https://www.cnblogs.com/shenben/p/6814542.html
Copyright © 2011-2022 走看看