堆排序(優先佇列) 合併果子

2021-07-22 05:34:55 字數 1230 閱讀 7455

首先說一下堆的性質:分為大根堆和小根堆,根節點都是最值,小根堆的根節點是最小的,每個堆都比它的兩個子堆要小,大根堆的根節點是最大的,每個堆都比它的兩個子堆要大。

順便說一下二叉樹的性質,左子樹小於根節點小於右子樹,所以都要先序遍歷。

合併果子,是指有n堆果子,每次合併兩堆,每次花費的力氣為兩堆之和,求合併為一堆後,花費的最小力氣。

顯然,每次合併最小的兩堆即可滿足題意。

又顯然,要用堆排序,這裡用小根堆。

(ps:堆跟二叉樹一樣,都是乙個一維陣列,每個點的左子樹(左堆)的編號等於它*2,右子樹(右堆)的編號等於它*2+1)

堆和樹的優勢:不需要像佇列一樣乙個個列舉 而是每次判斷將剩餘情況二分,能將o(n)變成o(logn)。(二分的思路有很多 堆和樹還有快排)

顯然二分的前提是佇列要有某種規律,比如需要先排好序。

比如乙個數列1,2,3,4,5,6,7,8,9,10;(a[1]~a[10])

比如要查詢8;

倘若列舉,顯然要8次;

如果二分,一開始的範圍顯然是全部(1~10)。

取中間a[(1+10)/2]=a[5]=5<8;

那麼範圍變為6~10。(一次查詢)

繼續查詢,在剩餘範圍二分。

a[(6+10)/2]=a[8]=8。找到目標(兩次查詢)

這就是二分的優點。

當然,倘若要尋找1,列舉會比二分快,但二分的優勢是整體優化,也就是說,在剛剛的樣例裡,顯然所有的數在4次以內都會被搜到,而列舉,最多需要10次。

下面上**:

#include#includeusing namespace std;

long long ans;

int a[110000],len,s;

int main()

}len=n;

while(len>1)

s+=a[1];//之前已經取出過一堆了 現在重新使堆滿足性質後 取出第二堆相加

a[1]=s;//將取出的兩堆合起來放回去,顯然此時可能不滿足堆的性質了,需要往下沉

ans+=s;//將花費的力氣加入ans

u=1;//此時可能需要下沉的堆的編號為1

while((a[u*2]a[u*2+1] && len>=u*2+1)u=u*2+1;

else u*=2;

k=a[u];

a[u]=a[u/2];

a[u/2]=k;//交換}}

cout<

合併果子(優先佇列)

在乙個果園裡,多多已經將所有的果子打了下來,而且按果子的不同種類分成了不同的堆。多多決定把所有的果子合成一堆。每一次合併,多多可以把兩堆果子合併到一起,消耗的體力等於兩堆果子的重量之和。可以看出,所有的果子經過n 1 n 1次合併之後,就只剩下一堆了。多多在合併果子時總共消耗的體力等於每次合併所耗體...

合併果子優先佇列

合併果子 每次取出兩個最小的,求和之後再放進去,再取出兩個最小的,依次進行下去。當然,每次取出之後都需要累加兩個數。本文使用優先佇列,也就是最小堆實現 typedef long long ll priority queue ll,vector greater pq 從小到大排序優先佇列的大小大於1p...

合併果子 貪心 優先佇列

c 合併果子 crawling in process.crawling failed time limit 1000msmemory limit 131072kb64bit io format lld llu submit status description 現在有n堆果子,第i堆有ai個果子。現...