首先說一下堆的性質:分為大根堆和小根堆,根節點都是最值,小根堆的根節點是最小的,每個堆都比它的兩個子堆要小,大根堆的根節點是最大的,每個堆都比它的兩個子堆要大。
順便說一下二叉樹的性質,左子樹小於根節點小於右子樹,所以都要先序遍歷。
合併果子,是指有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個果子。現...