AcWing 149. 荷马史诗
题面:
一部《荷马史诗》中有种不同的单词,从 到 进行编号。其中第 种单词出现的总次数为 。用 进制串 来替换第 种单词,使得其满足如下要求:
- 对于任意的
, ,都有: 不是 的前缀。 - 替换以后得到的新的《荷马史诗》长度最小。
- 在确保总长度最小的情况下,求出最长的
的最短长度。
原题链接:AcWing 149. 荷马史诗
参考链接:
AcWing 149. 荷马史诗(二叉堆 Huffman树 贪心) - Bug-Free
AcWing 149. 荷马史诗 - 秦淮岸灯火阑珊
本题面对的两个问题:
- 最小权值和
- 最小化深度
最小权值和
进制编码的情况下,使用的不再是二叉哈夫曼树,而是 叉堆。 叉堆会存在的问题:只能形成完全 叉堆的特殊情况- 形成满
叉堆的条件:
⇒
⇒ 为 的倍数
⇒ - 解决方法:
- 法一:特殊处理首次合并,首次合并为
堆,剩下合并为 堆。 - 法二:为最后一层补满空结点,以让其余结点权值和最小化。
- 法一:特殊处理首次合并,首次合并为
- 形成满
最小化深度
- 在建树时,新节点优先成为深度较小的结点的孩子。
- 如果多个点的权值相同时,优先考虑高度较低(已合并次数最少)的树。
- Q:为什么代码中使用
max(u, h.top().second)
而不是min
?
A:因为建树的过程是从下往上的。
- Q:为什么代码中使用
拓展:Huffman树与Trie树
常见应用场景
深度与权重:Huffman树
求公共前缀:Trie树
本题为何适用
本题中要求对于任意的
应用Trie树:叶子结点上的字母必然互不相同;
应用Huffman树:每层至多产生一个父结点。
代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL, int> PLI; const int N = 1e5 + 10; int n, k; LL x; priority_queue<PLI, vector<PLI>, greater<PLI>> h; int main() { cin >> n >> k; for (int i = 0; i < n; i++) { cin >> x; h.push({ x,0 }); } while ((n - 1) % (k - 1)) { h.push({ 0,0 }); //加入空结点 n++; //更新结点数 } LL res = 0; while (h.size() > 1) { LL sum = 0; int u = 0; //新节点的高度 for (int i = 0; i < k; i++) { sum += h.top().first; //该层的所有结点权值和 u = max(u, h.top().second); //同权值时优先深度最小 h.pop(); } res += sum; h.push({ sum,u + 1 }); } cout << res << endl; cout << h.top().second; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!