ACM水题-合并果子(堆结构,贪心算法,AC)

来源:互联网 发布:四十而立五十而知天命 编辑:程序博客网 时间:2024/04/29 19:47

合并果子

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) ;}}