题目
思路
如果我们最后编码的答案建成一个trie树,
一条路径上除了末尾可以是一个编码的结尾
这条路径上的任何一个点都不可能是一个编码的结尾
因为需要满足一个编码不是另一个编码的前缀
如果我们视每一个点的点权为这个点出现的次数
每一个单词的编码为根节点到叶子节点上路径上的字符
也就是它的长度就是它的深度,也就是问题1可以表示为
\(ans=\sum_{i=1}^{n}dep_i*w_i\)
需要最小,
\(w_i\)是给定的,我们能做的只是调整每个编码的长度,
也就是末端节点再树上的深度
感性理解一下
就是\(w_i\)越大的点它的\(dep_i\)越小
\(w_i\)越小的点它的\(dep_i\)越大
我们还需要使最大的长度最短
很明显的一点,每一个节点的分支一定是尽可能多的
所以我们可以将\(w\)从小到大排序
用优先队列来维护
取出前k个点,将他们的权值累加,建一个新点,再将这个点重新塞回优先队列中
如此反复,直至优先队列中只剩下1个点
很明显权值越大的距离根节点的距离一定越近
如果是我们建的是虚点呢?也无妨
因为我们还需要保证一个编码不是另一个编码的前缀
此方法也可称为huffman编码
代码
#include<iostream>
#include<queue>
using namespace std;
struct node
{
int dep;
long long w;
friend bool operator < (const node &a,const node &b)
{
if(a.w==b.w)
return a.dep>b.dep;
return a.w>b.w;
}
}a[100005];
int n,k;
long long ans;
node cnt[15];
priority_queue<node> q;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<=n;i++)
{
long long w;
cin>>w;
q.push((node){0,w});
}
while((n-1)%(k-1))
{
n++;
q.push((node){0,1ll*0});
}
while(q.size()!=1)
{
for(int i=1;i<=k;i++)
{
cnt[i]=q.top();
q.pop();
}
node t;
t.dep=0;
t.w=0;
for(int i=1;i<=k;i++)
{
t.w+=cnt[i].w;
t.dep=max(t.dep,cnt[i].dep);
}
t.dep++;
q.push(t);
ans+=t.w;
}
cout<<ans<<' '<<q.top().dep;
return 0;
}
原文地址:https://www.cnblogs.com/loney-s/p/12076863.html
时间: 2024-10-09 10:01:00