• 哈夫曼树


    先给出哈夫曼树的定义:构造一颗包含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
  • 相关阅读:
    IntelliJ IDEA更新maven依赖包
    PHP运行出现Notice
    JetBrains CLion
    SQL中GROUP BY的使用
    LCA 总结
    HAProxy:第三方包及编译安装
    nginx之升级openssl及自定义nginx版本
    nginx之http反向代理多台服务器
    nginx之rewrite相关功能
    nginx之rewrite及防盗链
  • 原文地址:https://www.cnblogs.com/gcfer/p/12715594.html
Copyright © 2020-2023  润新知