zoukankan      html  css  js  c++  java
  • 合并果子(单调队列优化)

      题意:

      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 ;

  • 相关阅读:
    悼念丹尼斯·里奇,那个给乔布斯提供肩膀的巨人(转载)
    c# 做成Windows服务
    Visual Studio 2010 新建完项目编译就出错
    C#调用Win32 的API函数User32.dll
    c# Remoting小例子
    backgroundworker使用 实现进度条ProgressBar
    winform最小化后隐藏到右下角,单击或双击后恢复
    关于Thread的实例
    c# 捕获的异常写到日志里
    C# delegate and event 规范写法
  • 原文地址:https://www.cnblogs.com/zub23333/p/8568544.html
Copyright © 2011-2022 走看看