洛谷 P2168 [NOI2015]荷马史诗 解题报告

P2168 [NOI2015]荷马史诗

题目描述

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

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

一部《荷马史诗》中有\(n\)种不同的单词,从\(1\)\(n\)进行编号。其中第i种单 词出现的总次数为\(w_i\)。Allison 想要用\(k\)进制串\(s_i\)来替换第i种单词,使得其满足如下要求:

对于任意的 \(1\le i, j \le n\)\(i ≠ j\) ,都有:\(s_i\)不是\(s_j\)的前缀。

现在 Allison 想要知道,如何选择\(s_i\),才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,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\)个字符组成的字符串。

输入输出格式

输入格式:

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

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

输出格式:

输出包括 2 行。

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

第 2 行输出 1 个整数,为保证最短总长度的情况下,最长字符串 si 的最短长度。

说明:


如果把编码集合建成字典树,满足所有的编码不是别的的前缀即为所有的编码都在叶子节点结束

总代价为\(\sum w_i*len_i\)\(len_i\)为长度

其实就是在求\(k\)叉哈弗曼树

我们将节点补全至满足\((k-1)|(n-1)\),保证上面的叶子都填满了

然后从小的值开始放叶子节点


Code:

#include <cstdio>
#include <queue>
#define ll long long
using namespace std;
struct node
{
    ll w,d;
    bool friend operator <(node n1,node n2)
    {
        return n1.w==n2.w?n1.d>n2.d:n1.w>n2.w;
    }
}t;
priority_queue <node> q;
int n,k;
int main()
{
    scanf("%d%d",&n,&k);
    t.d=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&t.w);
        q.push(t);
    }
    t.w=0;ll ans=0,mx=0;
    while((n-1)%(k-1)!=0) q.push(t),++n;
    while(q.size()>1)
    {
        t.d=t.w=0;
        for(int i=1;i<=k;i++)
        {
            t.w+=q.top().w,t.d=max(q.top().d,t.d);
            q.pop();
        }
        ++t.d,ans+=t.w,mx=max(mx,t.d);
        q.push(t);
    }
    printf("%lld\n%lld\n",ans,mx);
    return 0;
}

2018.8.31

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9568393.html