[貪心] 合併果子
BZOJ 3669 合併果子
題目描述
在一個果園裡,多多已經將所有的果子打了下來,而且按果子的不同種類分成了不同的堆。多多決定把所有的果子合成一堆。
每一次合併,多多可以把兩堆果子合併到一起,消耗的體力等於兩堆果子的重量之和。可以看出,所有的果子經過n-1次合併之後,就只剩下一堆了。多多在合併果子時總共消耗的體力等於每次合併所耗體力之和。
因為還要花大力氣把這些果子搬回家,所以多多在合併果子時要儘可能地節省體力。假定每個果子重量都為1,並且已知果子的種類數和每種果子的數目,你的任務是設計出合併的次序方案,使多多耗費的體力最少,並輸出這個最小的體力耗費值。
例如有3種果子,數目依次為1,2,9。可以先將1、2堆合併,新堆數目為3,耗費體力為3。接著,將新堆與原先的第三堆合併,又得到新的堆,數目為12,耗費體力為12。所以多多總共耗費體力=3+12=15。可以證明15為最小的體力耗費值。
輸入
輸入包括兩行,第一行是一個整數n,表示果子的種類數。第二行包含n個整數,用空格分隔,第i個整數ai是第i種果子的數目。
1<=n<=10000,1<=ai<=20000
輸出
輸出包括一行,這一行只包含一個整數,也就是最小的體力耗費值。輸入資料保證這個值小於2^31。
樣例輸入
3
1 2 9
樣例輸出
15
解題思路
首先看到這個題很容易想出一種貪心思路:
每一次合併之前,都將現在的沒堆果子從小到大排序,然後再合併最小的兩堆
其實這就是正解,是不是很簡單
由於資料範圍過大,嘗試一下就知道按照這種方法做肯定會超時,那麼我們就需要想更優的方法。但其實我們的貪心思想是正確的,而這道題難就難在此處
我們可以想一下有什麼資料結構可以自動排序,沒錯,聰明的你肯定會想到棧優先佇列
引入優先佇列之後,這道題就簡單多了,因為優先佇列的處理時間是很短的,所以根本不用考慮超時這一問題
所以這道題用優先佇列解的思路就是:
先將所有果子入隊,然後每次再取出前兩個元素,將它們的和入隊,最後剩下的一個元素就是答案
參考程式碼
#include<cstdio>
#include<queue>
#include<cstring>
#define reg register
using namespace std;
priority_queue<int, vector<int>, greater<int> > p; //定義小根堆
int n, x, y, ans;
inline void read(int &x){ //輸入優化
x = 0; int f = 1; char s = getchar();
while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
while(s >= '0' && s <= '9') {x = x * 10 + s - 48; s = getchar();}
x *= f;
}
inline void write(int x){ //輸出優化
if(x < 0) {putchar('-'); x = -x;}
if(x / 10) write(x / 10);
putchar(x % 10 + 48);
}
int main(){
read(n);
for(reg int i = 1;i <= n;i ++){ //初始入隊
read(x);
p.push(x);
}
for(reg int i = 1;i <= n - 1;i ++){ //求解
x = p.top(); //取出最小的兩個元素
p.pop();
y = p.top();
p.pop();
p.push(x + y); //將它們的和入隊
ans = ans + x + y;
}
write(ans);
putchar('\n');
return 0;
}