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

哈夫曼树

时间:2020-04-16 20:55:30      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:单词   ref   pair   答案   技术   有一个   event   轻松   证明   

先给出哈夫曼树的定义:构造一颗包含n个节点的k叉树其中每个叶子节点都有权值w[i],要求最小化所有叶子节点的w[i]*deep[i]之和.该问题的解被称为k叉哈夫曼树.

先来说两个引理:

1.权值最小的节点深度必定最大.

证明:我们设x,y.使得w[x]>w[y].但deep[x]>deep[y]如若交换x,y由于y的去权值更小,所以只会使得答案更优.所以只要存在权值大,深度大的节点,我们便可以通过交换使得答案

更优.

2.满足最优子结构

证明:我们可以将一些点合并之后,权值定义为这些点的权值和.我们发现最优解一定遵从引理1.即如果有深度,权值大于我们合并之后的点的权值与深度.我们照样可以将通过交换使得答案更小.且我们已经通过合并,此时权值是合并的点的和,发现这样对答案并无影响.

重要的是哈夫曼树的解法。

由于满足最优子结构,我们可以构造一个贪心算法,又由于满足引理1,我们可以很轻松的想到每次找出最小的若干个节点将他们合并,再将他们放回原来的集合中即可.

发现这可以用小根堆优化.

接下来有两道例题:

1.148. 合并果子

这个算是很水的题了,但仍要知其所以然..

我们考虑所有的合并操作实际上构成了什么,一棵树,n-1次操作实际上是树边.

之后考虑怎样产生的答案,考虑一个果子,他虽然合并成其他果子,但他所在的果子每次合并都是累加下它的代价,由于合并操作实际上就是树边,我们可以想到代价就是权值*操作次数=权值*树边,符合哈夫曼树的定义.由于每次只能合并两堆果子,所以这是一个满二叉树.

技术图片
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,w[N],sum;
priority_queue<int>q;
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i) cin>>w[i],q.push(-w[i]);
    for(int i=1;i<n;++i)
    {
        int x1=-q.top();q.pop();
        int x2=-q.top();q.pop();
        sum+=x1+x2;
        q.push(-(x1+x2));
    }
    cout<<sum<<endl;
    return 0;
}
View Code

 

2.荷马史诗

很巨的题,题目意思真的饶...

我们考虑题面要求我们用k进制串表示若干个单词,使得总长度最小,且没有一个串是另一个串的前缀.

我们考虑tire树,不能有前缀的意思就是每个单词必须是叶子节点,由于是k进制数,所以tire树的最大分支为k.考虑答案的组成.

每个单词的权值*代表单词的串的长度.而串的长度在tire树种就是到根节点的距离.嗯又是满足哈夫曼树的定义.

由于未必是满k叉树,所以我们可以加一些0节点,使得总结点数能构成满k叉树.因为这些0节点一定在最深的,且对答案不造成贡献.

这里顺便说一下,叶子节点满足(n-1)%(k-1)==0时才是满二叉树...

技术图片
#include<bits/stdc++.h>
#define ll long long

using namespace std;

int n,k;
priority_queue<pair<ll,int> >q;

int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;++i)
    {
        ll x;cin>>x;
        q.push({-x,0});
    }
    
    while((n-1)%(k-1)!=0) q.push({0,0}),++n;

    ll ans=0;int depth=0;
    while(q.size()>1)
    {
        ll sur=0;int maxdepth=0;
        for(int i=1;i<=k;++i)
        {
            sur+=-q.top().first;
            maxdepth=max(maxdepth,-q.top().second);
            q.pop();
        }
        q.push({-sur,-maxdepth-1});
        ans+=sur;depth=max(maxdepth+1,depth);
    }
    
    cout<<ans<<endl<<depth<<endl;
    return 0;
}
View Code

 

哈夫曼树

标签:单词   ref   pair   答案   技术   有一个   event   轻松   证明   

原文地址:https://www.cnblogs.com/gcfer/p/12715594.html

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