P2168 NOI2015 荷马史诗
#算法/哈夫曼树
思路:
显然,这题需要将每个单词换成独立且没有前缀的进制字符串,显然,我们可以联想起来二进制哈夫曼树,我们只需要将二进制转换成我们要求的进制即可,但是,会出现一个问题,如果是二进制,显然,任何数量结点最终都会变成两堆,然后合并成一堆,但是,如果是三进制,四进制…,显然,会出现无法恰好被合并成一堆的情况,那么,这会出现什么情况呢?
以三进制为例
假设有4个结点s1,s2,s3,s4
根据图示,我们会发现s1,s2,s3长度都为2,s4长度都为1
但是,如果我们补充结点s0,使得结点恰好被合并成一堆
我们可以根据图示得出
s1,s2长度为2,s3,s4长度为1
所以,在我们合并之前,需要先判断当前的结点是否能够恰好合并成一堆,如果无法恰好合并成一堆,我们需要补0,使得靠近根节点的结点尽可能多,不会出现空余的地方
1.定义结点
struct node {
//w表示权重ll w;
//h表示深度ll h;
//优先选择权重小的friend bool operator<(node n1, node n2) {
//如果权重相同,为了保证深度最小,优先将深度小的选出来if(n1.w==n2.w){return n1.h>n2.h;}else return n1.w > n2.w;}
}nodes[N];
2.保证结点数量恰好分配成一堆
for (int i = 0; (n - 1 + i) % (m - 1) != 0; i++) {cnt++;nodes[cnt].w = 0;nodes[cnt].h = 1;pq.push(nodes[cnt]);}
3.寻找最小m堆
while (pq.size() >= m) {ll w = 0;ll h = -1;for (int i = 1; i <= m; i++) {node tpq = pq.top();//我们每次找到子结点时,就将子节点的权重加起来,方便后序将子节点的深度加1w += tpq.w;pq.pop();h = max(tpq.h, h);}//子节点的所有深度加1,相当于将这些子节点的权重再加一遍sum += w;pq.push({ w,h + 1 });}
完整代码
#include<iostream>#include<string>#include<vector>#include<queue>using namespace std;int n, m;const int N = 1e5 + 30;typedef long long ll;ll cnt;struct node {ll w;ll h;friend bool operator<(node n1, node n2) {if(n1.w==n2.w){return n1.h>n2.h;}else return n1.w > n2.w;}}nodes[N];priority_queue<node>pq;int main(void) {cin >> n >> m;for (int i = 1; i <= n; i++) {cnt++;cin >> nodes[cnt].w;nodes[cnt].h = 1;pq.push(nodes[cnt]);}for (int i = 0; (n - 1 + i) % (m - 1) != 0; i++) {cnt++;nodes[cnt].w = 0;nodes[cnt].h = 1;pq.push(nodes[cnt]);}ll sum = 0;while (pq.size() >= m) {ll w = 0;ll h = -1;for (int i = 1; i <= m; i++) {node tpq = pq.top();w += tpq.w;pq.pop();h = max(tpq.h, h);}sum += w;pq.push({ w,h + 1 });}cout << sum << endl << pq.top().h - 1 << endl;}