二叉堆(合并果子)

二叉堆

二叉堆即是一棵完全二叉树,二叉堆有两种,一种是是最大堆,另一种是最小堆。
最大堆:堆顶大于或者等于任何一个子节点的值;
最小堆: 堆顶小于或者等于任何一个子节点的值。
二叉堆的性质主要是可以进行插入节点、删除节点、取出值最小的节点、减小节点的值等基本操作。

列题

合并果子
在一个果园里,达达已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。

达达决定把所有的果子合成一堆。

每一次合并,达达可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。

可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。

达达在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以达达在合并果子时要尽可能地节省体力。

假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使达达耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为129。

可以先将12堆合并,新堆数目为3,耗费体力为3。

接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。

所以达达总共耗费体力=3+12=15。

可以证明15为最小的体力耗费值。
输入格式
输入包括两行,第一行是一个整数n,表示果子的种类数。

第二行包含n个整数,用空格分隔,第i个整数ai是第i种果子的数目。

输出格式
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。

输入数据保证这个值小于231。

数据范围
1≤n≤10000,
1≤ai≤20000
输入样例:
3 
1 2 9 
输出样例:
15

题意
每次取最小的两堆果子,合并放入果堆
1+2=3
3+9=12

插入操作
建立小堆,输入一个数先插入到堆低,不断和自己的父亲比较,比父亲小,就进行交换位置,如果交换后或者是到了堆顶就插入完成了。

插入操作代码

void in(int num)
{
     
	int pre,pa;
	pre=++k;
	fru[k]=num;
	while(pre>1)
	{
     
 		pa=pre/2;
 		if(fru[pa]>fru[pre])
 		{
     
 			swap(fru[pa],fru[pre]);
 			pre=pa;
		 }
		 else return ;
 	}
}

AC代码 O(nlog n)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int fru[10010],k,n;
void in(int num)//插入一个数到合适的位置
{
     
	int pre,pa;
	pre=++k;
	fru[k]=num;
	while(pre>1)
	{
     
 		pa=pre/2;
 		if(fru[pa]>fru[pre])
 		{
     
 			swap(fru[pa],fru[pre]);
 			pre=pa;
		 }
		 else return ;
 	}
}
int down()//找到当前堆最小的数并删除
{
     
	int pre=1,pa,s;
	s=fru[1];
	fru[1]=fru[k];
	k--;
	while(pre*2<=k)
	{
     
		pa=pre*2;
		if(pa<k&&fru[pa+1]<fru[pa])pa++;
		if(fru[pre]<=fru[pa])return s;
		swap(fru[pre],fru[pa]);
		pre=pa;
	}
	return s;
}
int main()
{
     
	int i,j;
	while(~scanf("%d",&n))
	{
     
		int m,sum=0;
		for(i=1;i<=n;i++)
		{
     
			scanf("%d",&m);
			in(m);			
		}
		for(i=1;i<n;i++)
		{
     
			int a;
			int b;
			a=down();
			b=down();
			sum+=a+b;
			in(a+b);
		}
		printf("%d\n",sum);
	}
	
}

你可能感兴趣的:(二叉堆(合并果子))