1. 程式人生 > >CSU-1588 合併果子(堆/單調佇列)

CSU-1588 合併果子(堆/單調佇列)

題意

n 堆果子,第 i 堆果子有 ai 個,每次合併的代價是兩堆果子個數的總和,求總合並的最小代價。
1n1000

思路

堆(優先佇列)的做法已經眾所周知,複雜度是不可避免的 O(nlogn) 。但如果用單調佇列,可以在 O(n) 的複雜度中解決。
不難發現,每次合併產生的代價總是單調不減的,那我們可以考慮開另一個佇列 b 。一開始從排序後的 a 佇列中取兩個隊首最小元素,並將其推入 b 佇列。以後每次都從 ab 隊首分別去出兩個較大的元素,並累計代價再插入 b 佇列尾。

程式碼

#include<iostream>
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x);i<=(y);i++) #define DOR(i,x,y) for(int i=(x);i>=(y);i--) #define N 1003 typedef long long LL; using namespace std; int a[N],b[N]; int main() { int
T,n; scanf("%d",&T); while(T--) { int ans=0; scanf("%d",&n); FOR(i,1,n)scanf("%d",&a[i]); int al=1,ar=n,bl=1,br=0; sort(a+1,a+1+n); FOR(i,2,n) { int ele=0; if(bl>br||al<=ar&&a[al]<b[bl]) ele+=a[al++]; else
if(bl<=br) ele+=b[bl++]; if(bl>br||al<=ar&&a[al]<b[bl]) ele+=a[al++]; else if(bl<=br) ele+=b[bl++]; ans+=(b[++br]=ele); } printf("%d\n",ans); } return 0; }