题目四:果子合并(***)
[问题描述]
n堆果子, 每堆果子数量任意,试设计一种最佳方案,将这n堆果子合并为一堆,使得合并工作量最小。
注:规定合并两堆果子的工作量是这两堆果子的数量之和。
[标准输入]
M,N 。 M表示M组测试数据,N表示每组测试数据数量有N个,每堆果子数量不超过10000。随后的M行是测试数据。
[标准输出]
M行数据表示对应果子的合并工作量
[输入样例]:
2
4
7 5 2 4
5
5 6 2 9 7
【输出样例】:
35
65
#include<stdio.h>
#include<string.h>
const int MAXN=100000;
int heap[MAXN],size=0;
void push(int x){
int i=size++;//把新加入的数据放进最后一个叶子节点
while(i>0){
int p=(i-1)/2;
if(heap[p]<x) break;
heap[i]=heap[p];
i=p;
}//谁小谁上去
heap[i]=x;
}
int pop(){
int ret=heap[0];//把根提出来
int x=heap[--size];//最后一个叶子节点提到根的位置
int i=0;
while(i*2+1<size){//谁小谁上去
int a=i*2+1,b=i*2+2;
if(b<size&&heap[b]<heap[a]) a=b;
if(heap[a]>=x) break;
heap[i]=heap[a];//数据提上去
i=a;//下标改变
}
heap[i]=x;
return ret;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(heap,0,sizeof(heap));
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
int temp;
scanf("%d",&temp);
push(temp);
}
int ans=0;
while(size>1){//每次提出最小的两个合并后把新生成的节点放在叶子节点的位置
int a=pop();
int b=pop();
ans=ans+a+b;//计算代价
push(a+b);
}
printf("%d\n",ans);
}
return 0;
}
总结
经典的贪心思想,与哈夫曼树一样,考虑到最先调用过的以后还会再,越靠近根的位置累计调用次数越少,那么可以让调用代价小的先合并,所以手写一个堆即可,之后把每两个最小的堆顶合并成新结点加入到堆中。用ans累加这种新结点即可得到最终答案。