Huffman

问题:构造一颗包含 n 个叶子节点的 k 叉树,第 i 个叶子节点深度 di, 权重wi,使i=1ndiwi最小
直观考虑:要使得权重大的在上面,权重小的在下面
因为对于一个叶子节点,他的贡献是他的权重*他到根的路径节点数,不妨使树转化为:
对每个叶子节点,使得他和根节点之间的所有节点(除去自己)都加上这个叶子结点的权重,就可以把贡献分摊到所有节点上
这样最终的总和就是所有节点的和
于是就有了一个贪心策略:
1. 建立小根堆,插入所有节点
2. 找到 k 个权重最小的节点,新加入一个节点,权重为他们之和
3. 重复 2 操作,直到堆的大小为 1

但是这个算法有一个bug:可能在深度为2时节点的数量已经不足k
这样浪费了深度为2的空间,显然不是最小的构造

怎么解决?

肯定要使最底下一层的一些节点变成空,于是想到加入 0 元素,使得叶节点数 n 满足(n1)%(k1)=1

NOI2015 荷马史诗:

条件是没有一个字符串是另一个字符串的前缀,其实就是Huffman
最长si最短的限制?如果有两个相同的元素优先使用当前深度较小的元素

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std ;
typedef long long ll ;
const int N = 100100 ;
struct node {
ll w ; int tim ;
};
bool operator <(node a, node b) {
if (a.w != b.w) return a.w > b.w ;
else return a.tim > b.tim ;
}
priority_queue <node> q ;
int n, k ;
ll ans ;
ll w[N] ;
int main() {
scanf("%d%d", &n, &k) ;
for (int i = 1; i <= n; i++) scanf("%lld", &w[i]) ;
while ((n - 1) % (k - 1) != 0) w[++n] = 0 ;
for (int i = 1; i <= n; i++) q.push((node){w[i], 0}) ;
while (q.size() != 1) {
int mxt = 0 ; ll sum = 0 ;
for (int i = 1; i <= k; i++) {
sum += q.top().w ; mxt = max(mxt, q.top().tim) ;
q.pop() ;
}
q.push((node){sum, mxt + 1}) ;
ans += sum ;
}
printf("%lld\n%d\n", ans, q.top().tim) ;
}

__EOF__

作  者哈奇莱特
出  处https://www.cnblogs.com/lighthqg/p/17624946.html
关于博主:这个人很懒 什么也没有留下
版权声明:未获得本人同意请勿随意转载
声援博主:制作不易 点个赞吧

posted @   哈奇莱特  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
0
0
关注
跳至底部
点击右上角即可分享
微信分享提示