新知识:
转载博客:http://blog.163.com/sdnu_et/blog/static/13184636920100574953335/
哈夫曼树:建一棵树,使每个叶子节点的点权与深度的乘积之和最小,此树即为哈夫曼树。
建立二叉哈夫曼树:
假设有n个数a[1]~a[n],我们先建立有n棵树的森林,然后每次取出根的点权最小的两棵树将它们合并成一颗新的树将它放入森林中并删去原来的两棵树。
建立K叉哈夫曼树:
和二叉的差不多,因为有可能建到最后的时候,已经不足k个数了,所以我们在一开始就先取x个数,使剩下的n-x个数能够整除k。
不过也可以加上几个零,是n是k的倍数,反正零对答案没有什么影响。
那么具体怎么实现呢?
我们不可能每次都用堆来寻找前k小的数,复杂度不够优。
我们建立一个a数组和一个b数组,先用nlogn的复杂度,将a排序,然后每次取出k个数合并后,将它们放入b数组的后面,很显然b数组的大小是单调递增的。所以我们每次只要取出a和b队首的前k大的树,直到a清空,b数组中只有一个数为止。
这个的应用一般都是求合并n个数的最小值,或者是求最短的编码。
好啦,看例题吧:
http://www.lydsy.com/JudgeOnline/problem.php?id=4198
就是裸题了。
程序:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> #define maxn 100009 #include<queue> using namespace std; struct ding{ long long val; int tmp; }; long long a[maxn],b[maxn],c[maxn],sum,ans; int dep,h1,h2,t1,t2,node[maxn],n,k,now,num; priority_queue<ding>q; bool operator <(const ding&xx,const ding&yy) {return (xx.val==yy.val?xx.tmp>yy.tmp:xx.val>yy.val);} int main() { scanf("%d%d",&n,&k); long long x; for (int i=1;i<=n;i++) scanf("%lld",&x),q.push((ding){x,1}); sort(a+1,a+1+n); if ((n-1)%(k-1)) now=n+k-1-((n-1)%(k-1)); else now=n; for (int i=n+1;i<=now;i++) q.push((ding){0,1}); while (q.size()>1) { int t=0;sum=0; for (int i=1;i<=k;i++) { ding p=q.top(); q.pop(); t=max(t,p.tmp); sum+=p.val; } ans+=sum; q.push((ding){sum,t+1}); } printf("%lld\n",ans); printf("%d\n",q.top().tmp-1); return 0; }