一道很明显的哈夫曼树题
看discuz有人不知道为什么是哈夫曼树,这里稍微证明一下
题目说砍木板,收费等于要砍的木板的长度,要求费用最少
其实这里有点跟贪心的策略相同,总是要砍这些长度的,所以先把“长的”(不一定是单条长的,可以是合起来长的)先砍掉,这样很明显优于把“长的”留在后面砍的情况(因为那个长度至少要被收费两次)
现在回想一下哈夫曼树的结构,不知道哈夫曼树的请看:http://baike.baidu.com/view/127820.htm
哈夫曼树主要用于编码,用哈夫曼编码可以减少传输的数据量,特点是编码的长度不同
哈夫曼的结构是每次把最小的两个出队合并,再压入队列,重复。
如果把哈夫曼树的建树过程反过来看,正好是我们要求的每次把最长的砍掉的情况。
可以发现哈夫曼的结构保证小的总在下面,大的在上面,正好符合题目要求。可以证明要求的费用正好等于哈夫曼树的中间节点的和(除去叶子节点)。
不会证明的画个哈夫曼树,然后比较一下哈夫曼树跟最小费用砍法就能明白了。
实际上不用真正建树,只要把中间节点值加起来就行了。
建树是用堆模拟的优先队列可以提高效率,毕竟每次查询最小值是o(lgN)
下面给出代码,手写的优先队列,结果要用long long。
#include <stdio.h>
int piece[20010];
void heap(int n,int N)
{
int i=0,x=n;
if(2*n+1<N && piece[2*n+1]<piece[x])
{
x=2*x+1;
}
if(2*n+2<N && piece[2*n+2]<piece[x])
{
x=2*n+2;
}
if(x!=n)
{
piece[x]=piece[x]^piece[n];
piece[n]=piece[x]^piece[n];
piece[x]=piece[x]^piece[n];
heap(x,N);
}
return;
}
void buildheap(int N)
{
int i;
for(i=N>>1;i>=0;--i)
{
heap(i,N);
}
return;
}
long long hafuman(int N)
{
int i=0,length=N;
long long sum=0,tmp=0;
buildheap(length);
for(i=1;i<N;++i)
{
tmp=piece[0];
--length;
piece[0]=piece[length];
heap(0,length);
tmp+=piece[0];
piece[0]=tmp;
heap(0,length);
sum+=tmp;
}
return sum;
}
int main()
{
int N,i=0;
scanf("%d",&N);
for(i=0;i<N;++i)
{
scanf("%d",piece+i);
}
printf("%I64d\n",hafuman(N));
}