题目
思路
点此查看有意思的东西:哈夫曼编码。其证明是非常有意思的。接下来是正文:
感谢大佬的博客提供了思路与代码。
这道题其实就是一道 k k k 叉哈夫曼树。其证明过程,完全可以仿照普通哈夫曼树的证明。
于是我们直接建立哈夫曼树即可。至于最长的最小这一限制……只需要存一个树高即可。
我在这里进行简单的证明:
- 贪心选择性质:将 “高度” 看做一个节点的附加值,那么,最深的 k k k 个叶子(为什么是 k k k 个?因为最深的叶子的兄弟都是叶子)一定是 “高度” 最小的。(当然,前提都是其 ∑ w \sum w ∑w 最小)否则将其交换。
- 最优子结构性质:将 k k k 个点的父亲视做“高度”为自己的 “高度” + 1 +1 +1 的点,显然,这样的一个编码树与原来的树 “长相类似” 。
代码
大佬的码风好评!很简短。但是我还是想不到
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <utility>
#define mp(A,B) make_pair(A,B)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
priority_queue<pll> pq;
ll n,k;
pll x,y,ans;
int main()
{
scanf("%lld%lld",&n,&k);
ll i,a;
for(i=1;i<=n;i++) scanf("%lld",&a),pq.push(mp(-a,0));
while(k!=2&&n%(k-1)!=1) pq.push(mp(0,0)),n++;
while(pq.size()!=1)
{
x=y=mp(0,0);
for(i=1;i<=k;i++)
{
y=pq.top(),pq.pop();
x.first+=y.first,x.second=min(x.second,y.second-1);
}
ans.first-=x.first,ans.second=max(ans.second,-x.second),pq.push(x);
}
printf("%lld\n%lld",ans.first,ans.second);
return 0;
}