习题:荷马史诗(哈夫曼树&贪心)

Posted loney-s

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了习题:荷马史诗(哈夫曼树&贪心)相关的知识,希望对你有一定的参考价值。

题目

传送门

思路

如果我们最后编码的答案建成一个trie树,

一条路径上除了末尾可以是一个编码的结尾

这条路径上的任何一个点都不可能是一个编码的结尾

因为需要满足一个编码不是另一个编码的前缀

如果我们视每一个点的点权为这个点出现的次数

每一个单词的编码为根节点到叶子节点上路径上的字符

也就是它的长度就是它的深度,也就是问题1可以表示为

(ans=sum_{i=1}^{n}dep_i*w_i)

需要最小,

(w_i)是给定的,我们能做的只是调整每个编码的长度,

也就是末端节点再树上的深度

感性理解一下

就是(w_i)越大的点它的(dep_i)越小

(w_i)越小的点它的(dep_i)越大

我们还需要使最大的长度最短

很明显的一点,每一个节点的分支一定是尽可能多的

所以我们可以将(w)从小到大排序

用优先队列来维护

取出前k个点,将他们的权值累加,建一个新点,再将这个点重新塞回优先队列中

如此反复,直至优先队列中只剩下1个点

很明显权值越大的距离根节点的距离一定越近

如果是我们建的是虚点呢?也无妨

因为我们还需要保证一个编码不是另一个编码的前缀

此方法也可称为huffman编码

代码

#include<iostream>
#include<queue>
using namespace std;
struct node
{
    int dep;
    long long w;
    friend bool operator < (const node &a,const node &b)
    {
        if(a.w==b.w)
            return a.dep>b.dep;
        return a.w>b.w;
    }
}a[100005];
int n,k;
long long ans;
node cnt[15];
priority_queue<node> q;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        long long w;
        cin>>w;
        q.push((node){0,w});
    }
    while((n-1)%(k-1))
    {
        n++; 
        q.push((node){0,1ll*0});
    }
    while(q.size()!=1)
    {
        for(int i=1;i<=k;i++)
        {
            cnt[i]=q.top();
            q.pop();
        }
        node t;
        t.dep=0;
        t.w=0;
        for(int i=1;i<=k;i++)
        {
            t.w+=cnt[i].w;
            t.dep=max(t.dep,cnt[i].dep);
        }
        t.dep++;
        q.push(t);
        ans+=t.w;
    }
    cout<<ans<<' '<<q.top().dep;
    return 0;
}

以上是关于习题:荷马史诗(哈夫曼树&贪心)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4198 [ Noi 2015 ] 荷马史诗 —— 哈夫曼编码(k叉哈夫曼树)

荷马史诗 (K叉哈夫曼树+哈夫曼编码)

BZOJ 4198[Noi2015]荷马史诗 哈夫曼编码

NOI2015 荷马史诗 k-哈夫曼树

[哈夫曼树][优先队列] Bzoj P4198 荷马史诗

4198: [Noi2015]荷马史诗 (哈夫曼树基础)