greedy3253

如果只是单纯地每次切出一个最大的最终小木块,那么显然不对。比如上图中,如果从39里面切除20之后,得到19,如果只是切除最大小木块即5,那么得到14与5,后面对14进行切除,在切除5、5、4,共需要(39+19+14+9),如果按照哈弗曼切除,则为(39+19+10+9),显然后者要小。因此简单地切除当前最大块是不对的。

从上面的例子也可以看出,要保证越是大的木块,应该在哈弗曼树中的层数越少,即今早把它区分开来,否则停留在原始大块里增加切除负担。实际上,为了总和最小,的确应该每次找出最大的,但不是简单地只是找出一个,而考虑到对于一个最终小木块来说,每个小木块一定是由一个较大木块分成两部分得到的其中一部分,因此用哈弗曼树保证总和最小(哈弗曼树本质就是保证二叉树的权值和最小。)

#include <iostream>

#include <queue>

#include <cstdio>

using namespace std;

int main(){

int n;

int nCase;

scanf("%d",&nCase);

for(int i=0;i<nCase;i++){

scanf("%d",&n);

priority_queue<long long,vector<long long>,greater<long long> > qu;

while(n-->0){

long long x;

scanf("%lld",&x);

qu.push(x);

}

long long a=0,b=0;

long long res=0;

if(qu.size()==1)res=qu.top();//由于最底层的顶点(原始序列)都加进来了,因此最顶层顶点不用考虑了(前者所有原始序列之和即为顶层顶点数据。)

while(1){

a=qu.top();

qu.pop();

if(qu.empty())break;

b=qu.top();

qu.pop();

res+=a+b;

qu.push(a+b) ;

}

printf("%lld\n",res);

if(i!=nCase-1)printf("\n");

}

return 0;

}

时间: 2024-10-05 17:34:03

greedy3253的相关文章