首页 > 其他分享 >最优二叉树(Huffman 树)

最优二叉树(Huffman 树)

时间:2023-09-22 23:56:53浏览次数:38  
标签:le val int long dep 二叉树 最优 Huffman 节点

题目

\(k\) 叉树 \(T\) 有 \(n\) 片树叶。每片树叶 \(v_i\) 的权为 \(w_i\),深度为 \(l(v_i)\)。\(T\) 的权值为 \(W = \sum w_i\ l(v_i)\)。

求 \(W\) 的最小值。和在保证 \(W\) 最小的情况下,\(\max l(v_i)\) 的最小值。

数据范围:\(1 \le n \le 10^5\),\(2 \le k \le 10\),\(0 < w_i \le 10^{11}\)。

题目:[NOI2015] 荷马史诗


思路

请去翻《离散数学(第二版)》(屈婉玲著)P336。

为了保证 \(W\) 的值最小。每次贪心地选取最小的 \(k\) 个值进行合并,得到新节点,其权值为 \(k\) 个节点权值和。将新节点加入到队列中。重复上述步骤直到只剩一个节点为止。

\(W\) 等于所有分支节点(不含叶节点)的权之和。


而在非 \(2\) 阶哈夫曼树的情况下,最后一次合并的过程时,可能出现不足 \(k\) 个值的情况。解决办法是,添加权值为 \(0\) 的虚拟节点,以保证根节点的度数必然为 \(k\) 个。

第一次合并减少了 k 个叶节点,从第二次合并开始,每次减少 1 个新节点和 k-1 个叶节点。因此,最后一次合并时,剩余的叶节点个数为 (n-1)%(k-1)

如果剩余叶节点个数为 \(0\),说明能保证根节点的度数为 \(k\)。否则,应该加上 (k-1)-(n-1)%(k-1) 个虚拟节点,以保证最后一次合并时一共有 k-1 个叶节点。


为了保证 \(\max l(v_i)\) 最小。每次合并时,在 \(w_i\) 相同的情况下,优先选择 \(l_i\) 的节点进行合并。

因为需要动态选择前 \(k\) 个最小值,所以使用堆(优先队列)进行维护,\(w_i\) 小的排在前面,\(w_i\) 相同的情况下 \(l_i\) 小的排在前面。


需要注意一点。数据范围是 \(0 < w_i \le 10^{11}\),因此需要开 \(long \; long\)。

十年 OI 一场空,忘开 long long 见祖宗。


代码

#include <queue>
#include <cstdio>
#define int long long
struct Node {
    int val, dep;
    // val 为权值 w[i],dep 为深度 l[i]。
    bool operator < (const Node &b) const {
        return val == b.val ? dep > b.dep : val > b.val;
        // 重载小于号。STL 的优先队列为大根堆,所以用大于号。
        // 优先按权值 val 从小到大排序,val 相等时 按照深度 dep 从小到大排序。
    }
};
std::priority_queue <Node> q;
// 选取前 k 个最小值,用优先队列进行维护。
int max(int x, int y) {
    return x > y ? x : y;
}
signed main() {
    int n, k, x;
    scanf("%lld%lld", &n, &k);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &x);
        q.push((Node) {x, 0});
        // 把所有叶节点加入队列中,初始深度都为 0。
    }
    x = (k - 1 - (n - 1) % (k - 1)) % (k - 1);
    for (int i = 1; i <= x; ++i) {
        q.push((Node) {0, 0});
        // 加入虚拟节点。
    }
    int ans = 0, maxd = 0;
    // ans 为哈夫曼树的权值,maxd 为最大深度。
    while (!q.empty()) {
        Node d = (Node) {0, 0};
        for (int i = 1; i <= k; ++i) {
            Node x = q.top(); q.pop();
            d.val += x.val;
            d.dep = max(d.dep, x.dep + 1);
            // 取出前 k 个节点,合并为 1 个新节点。
            // 新节点的权值为各个叶节点权值相加,新节点的深度为最深的叶节点的深度 + 1。
        }
        ans += d.val;
        maxd = max(maxd, d.dep);
        // ans 等于非叶节点的节点权值和。
        // maxd 为所有节点的最深深度 + 1。
        if (!q.empty()) q.push(d);
        // 如果只剩最后一个新节点,且没有叶节点时,跳出循环。
    }
    printf("%lld\n%lld\n", ans, maxd);
    return 0;
}

标签:le,val,int,long,dep,二叉树,最优,Huffman,节点
From: https://www.cnblogs.com/-AnHC-/p/17723451.html

相关文章

  • 【UVA 12676】Inverting Huffman 题解(哈夫曼树)
    静态霍夫曼编码是一种主要用于文本压缩的编码算法。给出的文本为由N个不同字符组成的特定大小,算法选择N个代码,每个代码对应一个不同的字符性格使用这些代码压缩文本。为了选择代码,算法构建一个有N片叶子的二元根树。对于N≥2,树可按如下方式构建。1.对于文本中的每个不同字符,构......
  • 代码随想录算法训练营-贪心算法-5|56. 合并区间、738. 单调递增的数字、968. 监控二叉
    56. 合并区间时间复杂度:O(nlogn)空间复杂度:O(logn),排序需要的空间开销1classSolution:2defmerge(self,intervals):3result=[]4iflen(intervals)==0:5returnresult#区间集合为空直接返回67int......
  • 124. 二叉树中的最大路径和
    二叉树中的路径被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中至多出现一次。该路径至少包含一个节点,且不一定经过根节点。路径和是路径中各节点值的总和。给你一个二叉树的根节点root,返回其最大路径和。示例1:输入:root=......
  • 二叉树
    二叉树搜索二叉树:左边比根小,右边比根大;子树也满足这个特征。搜索二叉树还有一个特征:去走一个中序是一个升序的状态。所以搜索二叉树可以叫做二叉排序树或二叉查找树。模板不喜欢用T了,因为喜欢用K(关键字)。推荐一般用BinarySearchTree,二叉搜索树,不要用SearchTreeBinary,因为简写出......
  • 《剑指Offer》-34-二叉树中和为某一值的路径
    思路要求是从根节点开始的路径,这会比从任意节点开始的路径简单很多思路是从根节点开始遍历每一条路径,如果和没有达到目标值就继续向下遍历大于就回退,等于就返回到结果集中,可以看到这是一个回溯动作实际过程中,首先不管是等于还是大于,回退pop()操作都要执行,这样才不会影响到后......
  • 找接口的最优吞吐量 每秒事务处理数
    1.循环并发在聚合报告中找到波动不大的吞吐量本次找到的是每秒处理3177个事务1秒发送1个请求永远循环  聚合报告 2预估并发是6000个,所以需要将线程数改成2 ......
  • 114. 二叉树展开为链表
    给你二叉树的根结点root,请你将它展开为一个单链表:展开后的单链表应该同样使用TreeNode,其中right子指针指向链表中下一个结点,而左子指针始终为null。展开后的单链表应该与二叉树先序遍历顺序相同。示例1:输入:root=[1,2,5,3,4,null,6]输出:[1,null,2,null,3,null,......
  • 剑指Offer面试题7:重建二叉树
    一、题目给定节点数为n的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。提示:1.vin.length== pre.length2.pre和vin 均无重复元素3.vin出现的元素均出现在 ......
  • 543. 二叉树的直径
    给你一棵二叉树的根节点,返回该树的直径。二叉树的直径是指树中任意两个节点之间最长路径的长度。这条路径可能经过也可能不经过根节点root。两节点之间路径的长度由它们之间边数表示。输入:root=[1,2,3,4,5]输出:3解释:3,取路径[4,2,1,3]或[5,2,1,3]的长度。......
  • EasyGBS安防视频监控有哪些存储方式,哪种存储方式最优
    EasyGBS视频监控系统涉及到大量的视频数据,需要对这些数据进行存储,以备日后查看或备份。视频监控的存储需求需要根据场所的实际情况进行选择,以保证监控数据的有效存储和日后的调阅、回溯。 当前视频监控的存储方式,通常有以下几种:1.硬盘录像机(DVR)存储:DVR利用硬盘来储存视频数据,......