題目:合並果子(貪心)
題意:
有n堆果子,每次合並可以選擇任意兩堆合並,每次合並的消耗的體力是這兩堆果子的重量和,求把n堆果子合並成1堆所需消耗體力的最小值。
輸入格式
輸入包括兩行,第一行是一個整數 n,表示果子的種類數。
第二行包含 n 個整數,用空格分隔,第 i 個整數 ai 是第 i 種果子的數目。
輸出格式
輸出包括一行,這一行只包含一個整數,也就是最小的體力耗費值。
輸入數據保證這個值小於 2^31。
數據范圍
1≤n≤10000,
1≤ai≤20000
輸入樣例:
3
1 2 9
輸出樣例:
15
樣例解釋:
第一次合並選擇1和2,消耗體力3,第二次合並選擇3和9,消耗體力12。總共消耗的體力為3 + 12 = 15。
題目分析:貪心。
解題步驟:
- 每次選擇序列中最小的兩個元素進行合並,序列中添加一個新的元素,即這兩個元素的和。
- 重復步驟1,直到序列中的元素為1個。
貪心策略證明:
如下圖,如果把合並的過程看成一棵樹,葉子節點為要合並的果子的數量。可以知道,越先合並的果子的深度越大,權重也會越大。例如下圖中,a和b在合並的時候會被加1次,(a + b)和c在合並的時候會被加1次,所以a加了兩次,b加了兩次,c加了一次。
- 設貪心得出的答案是cnt,本題的正確答案是ans。
- 由於本題的答案ans是所有合並方案的最小值,所以必有ans <= cnt。
- 設h(x)為x在樹中的深度,設在正確答案中有c < a且h(c) < h(a),則我們可以交換a和c的合並順序,交換不影響其他點的權重。交換后,數值小的點權重變大,數值大的點權重變小,且a和c的權重改變量是相等的,所以交換后能夠得到更小的答案。故有cnt <= ans。
- 由於ans <= cnt且cnt <= ans,得cnt = ans。
AC代碼:
#include<iostream>
#include<queue>
using namespace std;
void solve(){
int n;
scanf("%d", &n);
priority_queue<int, vector<int>, greater<int> > pq;
while(n--){
int x;
scanf("%d", &x);
pq.push(x);
}
int res = 0;
while(pq.size() > 1){
int x = pq.top(); pq.pop();
int y = pq.top(); pq.pop();
res += x + y;
pq.push(x + y);
}
printf("%d\n", res);
}
int main(){
solve();
return 0;
}
時間復雜度:O(NlogN)。
空間復雜度:O(N)。