合并果子
Time Limit:1000MS Memory Limit:65536K
Total Submit:285 Accepted:112
Description
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
Input
输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
Output
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。
Sample Input
3 1 2 9
Sample Output
15
Source
NOIP 2004
/* -----------------------------------------------------
先排序,每次找最少数量的两堆合并。再排序,再找最少数量的
两堆 。。。结果超时了。。。
-----------------------------
正确应该是先建立一个最小堆,然后取出根,再重新整理堆,
再取出来。。。取出根,整理堆这一个操作的时间复杂度为o(lgn),
要取n-1次,所以总的时间复杂的度o(nlgn)。
之前的想法错在,每一次取完之后,就再排序一次,排一次
的平均时间为o(nlgn),n-1次,所以就变成了O(n^2lgn)。。大了足足
一个数量级,所以肯定超时。
这里没有注意到的是,在一个最小堆的基础是,取走根,然后重新整理
堆这一个操作,时间只有和堆的高度有关。。。算法导论上面也有说
“总之,一个堆可以在O(lgn)时间内,支持大小为n的集合上的任意优先队列
操作”~~~~~Orz~~~~
状况:AC,15MS
----------------------------------------------------- */
#include<stdio.h>
#define PARENT(i) ((i)/2)
#define LEFT(i) (2*(i))
#define RIGHT(i) ((2*(i))+1)
long a[10002] ;
void MinHeapify(long i) ;
long HeapExtractMin() ;
void HeapInsert(long key) ;
int main(void)
{
long n = 0 ;
long i = 0 ;
long nTotal = 0 ;
long nLength = 0 ;
long nFir = 0 ;
long nSec = 0 ;
scanf("%ld",&n) ;
for(i = 1 ; i <= n ; ++i)
{
scanf("%ld",&a[i]) ;
}
a[0] = n ;
nLength = a[0] ;
for(i = nLength/2 ; i >= 1 ; --i)
{
MinHeapify(i) ;
}
while(a[0] > 1)
{
nFir = HeapExtractMin() ;
nSec = HeapExtractMin() ;
HeapInsert(nFir+nSec) ;
nTotal += nFir+nSec ;
}
printf("%ld\n",nTotal) ;
return 0 ;
}
void MinHeapify(long i)
{
long l = LEFT(i) ;
long r = RIGHT(i) ;
long lowest = 0 ;
long temp = 0 ;
do
{
l = LEFT(i) ;
r = RIGHT(i) ;
if(l <= a[0] && a[l] < a[i])
{
lowest = l ;
}
else
{
lowest = i ;
}
if(r <= a[0] && a[r] < a[lowest])
{
lowest = r ;
}
if(i != lowest)
{
temp = a[lowest] ;
a[lowest] = a[i] ;
a[i] = temp ;
i = lowest ;
lowest = 0 ;
}
}while(i != lowest) ;
}
long HeapExtractMin()
{
long nMin = a[1] ;
long temp = 0 ;
a[1] = a[a[0]] ;
a[0]-- ;
MinHeapify(1) ;
return nMin ;
}
void HeapInsert(long key)
{
long nTemp = 0 ;
long i = 0 ;
a[0]++ ;
a[a[0]] = key ;
while(i > 1 && a[PARENT(i)] > a[i])
{
nTemp = a[i] ;
a[i] = a[PARENT(i)] ;
a[PARENT(i)] = nTemp ;
i = PARENT(i) ;
}
}
/* -----------------------------------------------------
先排序,每次找最少数量的两堆合并。再排序,再找最少数量的
两堆 。。。结果超时了。。。
----------------------------------------------------- */
#include<stdio.h>
#define PARENT(i) ((i)/2)
#define LEFT(i) (2*(i))
#define RIGHT(i) (2*(i)+1)
long a[10004] ;
long nHeapSize = 0 ;
long nLength = 0 ;
long nBegin = 2 ;
void MaxHeapify(long i) ;
void BuildMaxHeap() ;
void HeapSort() ;
int main(void)
{
long n = 0 ;
long i = 0 ;
long nTotalStrenth = 0 ;
long nTemp = 0 ;
long nFruitSum = 0 ;
scanf("%ld",&n) ;
nLength = n ;
for(i = 1 ; i <= n ; ++i)
{
scanf("%ld",&a[i]) ;
}
HeapSort() ;
for(i =1 ; i <= n-1 ; ++i)
{
a[i+1] += a[i] ;
nFruitSum = a[i+1] ;
nTotalStrenth += nFruitSum ;
a[i]= 0 ;
if(i < n-2 && nFruitSum > a[i+2])
{
nBegin = i+1 ;
HeapSort() ;
}
}
if(1 == n)
nTotalStrenth = a[1] ;
printf("%ld\n",nTotalStrenth) ;
return 0 ;
}
void MaxHeapify(long i)
{
long l = LEFT(i) ;
long r = RIGHT(i) ;
long largest = 0 ;
long temp = 0 ;
while(i != largest)
{
if(l <= nHeapSize && a[l] > a[i])
{
largest = l ;
}
else
{
largest = i ;
}
if(r <= nHeapSize && a[r] > a[largest])
{
largest = r ;
}
if(i != largest)
{
temp = a[largest] ;
a[largest] = a[i] ;
a[i] = temp ;
MaxHeapify(largest) ;
}
}
}
void BuildMaxHeap()
{
nHeapSize = nLength ;
long i = 0 ;
for(i = nLength/2 ; i >= 1 ; --i)
{
MaxHeapify(i) ;
}
}
void HeapSort()
{
long i = 0 ;
long nTemp = 0 ;
BuildMaxHeap() ;
for(i = nLength ; i >= nBegin ; --i)
{
nTemp = a[1] ;
a[1] = a[i] ;
a[i] = nTemp ;
nHeapSize-- ;
MaxHeapify(1) ;
}
}