重编码-K
背景
小粽学习了哈夫曼树之后,自己设计了贪心算法,用两个队列就过掉了练习题。
小粽想:那堆的算法有什么用呢?为了解决小粽的疑惑,邓老师委托小莉命制了这道题目……
描述
有一篇文章,文章包含 nn 种单词,单词的编号从 11 至 nn,第 ii 种单词的出现次数为 wiwi。
现在,我们要用一个 kk 进制串(即只包含 0,1,...,k−10,1,...,k-1 的串) sisi 来替换第 ii 种单词,使其满足如下要求:对于任意的 1≤i<j≤n1≤i<j≤n,都有 sisi 不是 sjsj 的前缀(这个要求是为了避免二义性)。
你的任务是对每个单词选择合适的 sjsj,使得替换后的文章总长度(定义为所有单词出现次数与替换它的 kk 进制串的长度乘积的总和)最小。求这个最小长度。
字符串 S1S1(不妨假设长度为 nn)被称为字符串 S2S2 的前缀,当且仅当:S2S2 的长度不小于 nn,且 S1S1 与 S2S2 前 nn 个字符组组成的字符串完全相同。
输入格式
第一行一个整数 nn,表示单词种数。
第 2 行到第 n+1n+1 行,第 i+1i+1 行包含一个正整数 wiwi,表示第 ii 种单词的出现次数。
输出格式
表示整篇文章重编码后的最短长度
输入样例1
5 3
1
3
5
10
3
输出样例1
29
样例2
点此下载。
限制
对于 100% 的数据,满足 1≤n≤3×105,2≤k≤10,1≤wi≤1091≤n≤3×105,2≤k≤10,1≤wi≤109;
对于 40% 的数据,满足 n≤3000n≤3000;
对于 35% 的数据,满足 k=2k=2。
#include <queue>
#include <cstdio>
#include <stdlib.h>
#include <vector>
using namespace std;
typedef long long ll;
priority_queue<ll, vector<ll>, greater<ll> > Q;
// n: 初始点数
// k: 哈夫曼树的叉树
// w: w[i] 表示第i个点的点权
// 返回值:最终编码的长度
ll solve(const vector<ll>& w, int n, int k)
{
ll sum = 0;
for (int i = 0; i < n; ++i)
Q.push(w[i]);
if(k!=2){
int r = n%(k-1)==1 ? 0 : k-n%(k-1);
n += r;
while(r--)
Q.push(0);}
while(n>1)
{
ll newEle = 0;
for (int i = 0; i < k; ++i)
{
newEle += Q.top();
Q.pop();
}
sum += newEle;
Q.push(newEle);
n -= k-1;
}
return sum;
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
vector<ll> w;
w.reserve(n);
for (int i = 0; i < n; i++)
{
ll a;
scanf("%lld", &a);
w.push_back(a);
}
printf("%lld\n", solve(w, n, k));
return 0;
}
!!!注意K叉树K!=2时可能需要补0,否则结果不是最小。