【NOI2015T4】荷马史诗-优先队列实现K进制哈夫曼编码+贪心

测试地址:荷马史诗

做法:按照题目的定义,就是要求一个最优编码,最优编码就是哈夫曼编码,不懂的可以百度。我们可以通过构造哈夫曼树来找到最优编码,由于原来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\n%lld",ans,mxlen);
  
  return 0;
}


posted @ 2017-04-21 21:28  Maxwei_wzj  阅读(151)  评论(0编辑  收藏  举报