zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》0x17二叉堆 利用优先队列求k叉哈夫曼树的最优结构

    题目链接:https://www.acwing.com/problem/content/description/151/

    给定长度为n的序列,代表一个单词的出现次数,要求构造k叉哈夫曼树使得总权值最小,并且在权值最小的情况下问最小的高度是多少?

    我们可以考虑不断取k个数组成一个新的结点放入优先队列,但是根节点可能不满k叉,所以可以考虑在其中不超过k-1个零来上调权值比较大的结点,权值为零的结点肯定在最深层的一个父节点上。

    也只有这一个父节点是不满k叉的,这个可以通过贪心的思想进行验证,接下来考虑深度最小,我们在合并的时候,值不同的节点在同一轮取数中取出来之后无法更优,只能按照顺序拼接,但是

    如果结点的值一样的时候我们需要优先选择高度比较小的来合并,因为我们的哈夫曼树是从下到上构建的,高度大的数可以放到下一轮合并中,可以证明这样的选择更优,故在优先级队列中我们

    需要维护两个关键字,一个是当前树的权重和,一个是高度,使用pair<long long ,int>存储。

    其次,每一轮取数中height的更新方式是,新构造的结点的height是子节点的height的最大值+1,注意取出来的结点一定是已经满足上面两种关键字最优性的。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<queue>
    typedef long long ll;
    #define  PLI pair<ll,int>
    using namespace std;
    int main(){
        int n,k;
        cin>>n>>k;
        priority_queue<PLI,vector<PLI>,greater<PLI> > pq; 
        for(int i=0;i<n;i++){
            ll x;
            cin>>x;
            pq.push({x,0});
        }
        while((n-1)%(k-1))n++,pq.push({0,0});//保证k叉树每个结点都有k个叉,并且最深处的结点存在为0的点
        ll res=0;
        while(pq.size()>1){
            ll s=0;
            int height=0;
            for(int i=0;i<k;i++){//构造一个结点 
                PLI p=pq.top();
                s+=p.first;
                height=max(height,p.second);//计算结点的高度,等于子节点的最大高度加一 
                pq.pop();
            }
            res+=s;
            pq.push({s,height+1}); 
        } 
        cout<<res<<endl<<pq.top().second<<endl;
    } 
  • 相关阅读:
    更换glibc版本进行调试
    HDCTF 2020 Writeup
    Srop 原理与利用方法
    form使用DELETE被转换为GET的原因
    sqlite将时间转换为本地时间读取
    firewall-cmd命令
    windows创建系统服务命令
    Open_vSwitch操作
    Dublin Core
    CWM(Common warehouse metamodel)
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13158129.html
Copyright © 2011-2022 走看看