AcWing 149. 荷马史诗

题面:
一部《荷马史诗》中有 n 种不同的单词,从 1n 进行编号。其中第 i 种单词出现的总次数为 wi。用 k 进制串 si 来替换第 i 种单词,使得其满足如下要求:

  1. 对于任意的 1i,jnij,都有:si 不是 sj 的前缀。
  2. 替换以后得到的新的《荷马史诗》长度最小。
  3. 在确保总长度最小的情况下,求出最长的 si 的最短长度。

原题链接:AcWing 149. 荷马史诗
参考链接:
AcWing 149. 荷马史诗(二叉堆 Huffman树 贪心) - Bug-Free
AcWing 149. 荷马史诗 - 秦淮岸灯火阑珊

本题面对的两个问题:

  1. 最小权值和
  2. 最小化深度

最小权值和

  • k 进制编码的情况下,使用的不再是二叉哈夫曼树,而是 k 叉堆。
  • k 叉堆会存在的问题:只能形成完全 k 叉堆的特殊情况
    • 形成满 k 叉堆的条件:
      nm(k1)=1
      n1=m(k1)
      n1k1 的倍数
      (n1)%(k1)=0
    • 解决方法:
      • 法一:特殊处理首次合并,首次合并为 nm(k1) 堆,剩下合并为 k 堆。
      • 法二:为最后一层补满空结点,以让其余结点权值和最小化。

最小化深度

  • 在建树时,新节点优先成为深度较小的结点的孩子。
  • 如果多个点的权值相同时,优先考虑高度较低(已合并次数最少)的树。
    • Q:为什么代码中使用 max(u, h.top().second) 而不是 min
      A:因为建树的过程是从下往上的。

拓展:Huffman树Trie树

常见应用场景

深度与权重:Huffman树
求公共前缀:Trie树

本题为何适用

本题中要求对于任意的 1i,jnij,都有 si 不是 sj 的前缀。
应用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;
}
posted @   蒟蒻爬行中  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示