合并果子(单调队列优化)
题意:
n堆果子,每次可以选择两堆果子并将其合并成一堆果子,代价为新生成一堆的果子数。问最终合并成一堆的最小代价。(n<=1e5,Si<=2e5)(Si表示第i堆的果子数)
题解:
其实这题做法有很多。可以O(n^3)动规,可以O(n^2*logn)贪心,也可以用堆优化使得贪心的复杂度降到O(n*logn)。
但其实还有一种O(n)的合并方法——单调队列。
我们维护两个队列q1,q2,其中q1表示原本的那些堆(初始按升序排序),每次新合成的果子加入q2队尾。因为新合成的堆是越来越大的,所以q2也是有序的。那么我们很容易就可以从q1和q2中选出最小的两堆进行合并。
但是一开始题目给你的序列显然不是有序的对吧。如果用快排的话总复杂度就是O(n*logn+n),还不如堆优化。那么我们考虑桶排序,就可以把复杂度降为O(n+S)。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define LL long long #define RI register int using namespace std; const int INF = 0x7ffffff ; const int N = 10000 + 10 ; const int M = 20000 + 10 ; inline int read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == '-') f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-'0' ; return k*f ; } int n ; int num[M+10], hh[N] ; deque<int>q1 ; deque<int>q2 ; int main() { n = read() ; int x, y, ans = 0, mx = -INF ; memset(num,0,sizeof(num)) ; for(int i=1;i<=n;i++) { x = read() ; num[x]++ ; mx = mx > x ? mx : x ; } for(int i=1;i<=mx;i++) { while(num[i]) { q1.push_back(i) ; num[i] -- ; } } for(int i=1;i<n;i++) { if(!q2.size()) x = q1.front(), q1.pop_front() ; else if(!q1.size()) x = q2.front(), q2.pop_front() ; else { if(q1.front() < q2.front()) { x = q1.front(), q1.pop_front() ; } else x = q2.front(), q2.pop_front() ; } if(!q2.size()) y = q1.front(), q1.pop_front() ; else if(!q1.size()) y = q2.front(), q2.pop_front() ; else { if(q1.front() < q2.front()) { y = q1.front(), q1.pop_front() ; } else y = q2.front(), q2.pop_front() ; } ans += (x+y) ; q2.push_back(x+y) ; } printf("%d",ans) ; return 0 ; }
——end ;