码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-12-21 15:35:24      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:ons   long   习题   out   size   turn   cout   ios   problem   

题目

传送门

思路

如果我们最后编码的答案建成一个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;
}

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

标签:ons   long   习题   out   size   turn   cout   ios   problem   

原文地址:https://www.cnblogs.com/loney-s/p/12076863.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!