合并果子(单调队列优化)

  题意:

  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 ;
}
View Code

——end ;

原文地址:https://www.cnblogs.com/zub23333/p/8568544.html