测试地址:荷马史诗
做法:按照题目的定义,就是要求一个最优编码,最优编码就是哈夫曼编码,不懂的可以百度。我们可以通过构造哈夫曼树来找到最优编码,由于原来2进制编码时用到的哈夫曼树是二叉树,所以K进制编码时用到的哈夫曼树就是K叉树。K叉哈夫曼树的构造方法如下:用优先队列维护一个森林,一开始都是单点,存储每棵树的权值(在这道题里就是出现次数),每次从优先队列取出权值最小的K棵树,在它们上面增加一个根,合成一棵新树,新树的权值是K棵树权值的总和,将新树放入优先队列。当最后只剩下一棵树时,这棵树就是要求的哈夫曼树。不过,为了方便处理,由于每次要减少K-1棵树,最后要剩下一棵树,所以我们一开始要添加(K-1)-(N-1)%(K-1)个权值为0的辅助节点。答案可以在构造时同步统计,每次合并树时,答案就增加新树的权值那么多。然而还有第二个问,那么我们只需要再存储每棵树的深度(单点的深度为0),保证优先队列中的元素以权值为第一关键字升序排列,以深度为第二关键字升序排列,每次取出队首的K棵树合并即可。合并后新树的深度就是K棵树中最深的树的深度+1。最后第二个问的答案就是最后一棵树的深度。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int n,k;
long long ans,mxlen;
struct state
{
long long val,dep;
bool operator < (state a) const
{
if (val!=a.val) return val>a.val;
else return dep>a.dep;
}
};
priority_queue<state> q;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
long long a;
scanf("%lld",&a);
state s;
s.val=a,s.dep=0;
q.push(s);
}
int k0=((k-1)-(n-1)%(k-1))%(k-1);
for(int i=1;i<=k0;i++)
{
state s;
s.val=s.dep=0;
q.push(s);
}
while(!q.empty())
{
long long sum=0,mxdep=0;
bool flag=0;
for(int i=1;i<=k;i++)
{
state now=q.top();q.pop();
if (i==1&&q.empty()) {mxlen=now.dep;flag=1;break;}
sum+=now.val;
mxdep=max(mxdep,now.dep);
}
if (!flag)
{
state s;
s.val=sum,s.dep=mxdep+1;
q.push(s);
ans+=sum;
}
}
printf("%lld
%lld",ans,mxlen);
return 0;
}