1 条题解
-
0
自动搬运
来自洛谷,原作者为

Starlight237
[Drawer/auth]EgcqEzGZV+k435xMKiYIpABsjWY0ln/huv6DW7QW9Qw=搬运于
2025-08-24 22:25:09,当前版本为作者最后更新于2021-10-27 15:37:00,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Huffman 树
设一棵二叉树的叶节点权值和深度分别为 ,则定义 为这棵树的 WPL(这个名字并不准确,但是既然 OI-wiki 这么写,这里就沿用下来)。
给定所有叶节点和它们的 ,WPL 最小的二叉树称为这个叶节点权值序列的 Huffman 树。此外,这个二叉树应当满足除了叶节点的度为 0 以外,剩下的节点的度都是 2。
如下算法可以构建一个序列的 Huffman 树。
- 以给定的权值构造 n 棵只含有 1 个节点的二叉树,加入二叉树集合 。
- 从 中选取权值最小的两棵二叉树 合并成一个大二叉树 ,并令 。从 中删去 ,加入 。
- 重复上一个步骤,直到 中只剩下一棵二叉树。这棵二叉树就是 Huffman 树。事实上,合并过程中所有的 之和就是 Huffman 树的 WPL。
Huffman 编码
- 定义:将 个不同的字符映射为互不为前缀的二进制编码,称为这组字符的前缀编码。若这组字符构成一个字符串 ,记 表示将 中每个字符都替换成对应编码后 的长度。使 最小的前缀编码称为 Huffman 编码。
如下算法可以构造对于一个字符集 和字符串 的 Huffman 编码。
设需要编码的字符分别为 ,它们在 中出现的次数分别为 ,以 为叶节点, 为它的权值,构建 Huffman 树。规定连接父亲和左儿子的边权为 ,连接右儿子则为 ,令 的编码为从根节点走到 途径的边权构成的 字符串。容易证明这就是 Huffman 编码。并且 为 Huffman 树的 WPL。
回到本题:
(2021 集训队作业 P6915 【[ICPC2015 WF]Weather Report】)给定 种字符出现的概率,求所有 个可能的长度为 的字符串构成的集合的前缀编码,使得每个字符串的编码长度的期望值最小。
对于所有可能的字符串对应的概率求其 Huffman 树即可。但是 太多了,无法接受。注意到一个字符串的权值(概率)仅与其四个字符出现的次数有关,即可以用有序四元组 表达。每个四元组的概率都容易计算,并且该四元组对应的字符串数量为 。每个四元组作为一个状态 ,p 为它出现的概率,cnt 为它出现的次数,考虑对所有这些状态构建 Huffman 编码。
我们并不显式地建树,而只是开一个堆维护所有的 。
设堆顶为 ,若 ,对 的奇偶性进行讨论,若 为偶数,将 插入堆中,否则将 插入堆中,再转化为 为偶数的情况。
若 ,从堆中再取出一个状态 和当前状态合并。若 ,直接合并并且
continue,否则把状态拆成 和 。后者直接和当前状态合并,前者插入堆中。#include<bits/stdc++.h> using namespace std; #define ll long long #define pi pair<double, ll> const int N = 21; int n, C[N][N]; double p[4][N]; priority_queue<pi, vector<pi>, greater<pi> > Q; int main() { scanf("%d", &n); for (int i = 0; i < 4; ++i) { p[i][0] = 1; double pp; scanf("%lf", &pp); for (int j = 1; j <= n; ++j) p[i][j] = p[i][j - 1] * pp; } for (int i = 0; i <= n; ++i) C[i][0] = 1; for (int i = 1; i <= n; ++i) for (int j = 1; j <= i; ++j) C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; for (int i = 0; i <= n; ++i) for (int j = 0; j <= n - i; ++j) for (int k = 0, l; k <= n - i - j; ++k) l = n - i - j - k, Q.push(make_pair(p[0][i] * p[1][j] * p[2][k] * p[3][l], (ll)C[n][i] * C[n - i][j] * C[n - i - j][k])); double ans = 0; while (!Q.empty()) { pi tp = Q.top(); Q.pop(); if (Q.empty() && tp.second == 1) break; double pp = tp.first; ll cnt = tp.second; if (cnt > 1) { if (cnt & 1) Q.push(make_pair(pp, 1)), --cnt; ans += pp * cnt; Q.push(make_pair(pp * 2., cnt >> 1)); continue; } tp = Q.top(), Q.pop(); double qq = tp.first; cnt = tp.second; ans += pp + qq; Q.push(make_pair(pp + qq, 1)); if (cnt > 1) Q.push(make_pair(qq, cnt - 1)); } printf("%.6lf", ans); return 0; }
- 1
信息
- ID
- 6063
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者