zoukankan      html  css  js  c++  java
  • poj3017 Cut the Sequence 单调队列 + 堆 dp

    描述

      把一个正数列 $A$分成若干段, 每段之和 不超过 $M$, 并且使得每段数列的最大值的和最小, 求出这个最小值。

                    题目链接

    题解

      首先我们可以列出一个$O(n^2)$ 的转移方程 : $F_i = min( F_j + max( A_k ) )  $ $ j < i  &&   j < k <= i$

      然后我们可以考虑毒瘤优化。

      按照lyd的书中的思路, j 想要成为 可能的最优决策, 必须满足两个条件之一 :

    1.   j 是最小的使    $sumlimits_{k= j + 1}^ia_k  <= M$成立的数
    2.        $forall k in [j + 1, i] , A_j>A_k$

      可以用反证法来证明。

      对于第一个条件,可以在$O(n)$ 时间内求出所有的$j$, 并进行决策。

      接着构造一个单调队列, 满足 $j$ 递增, $A_j$ 递减 ——  若 $A[ j_1]< A[j _2]$则不满足第二个性质, 只能由让$j$ 满足第一个条件, 将$ j_1$弹出队列即可。

      查询在队列中的最优决策时, 队首不一定就是最有决策, 需要用 STL- set 来储存队列中的 $ F_j + max(A_k)$ $ j < i && j < k <=i$, 查询set中的最小值并更新答案。

      而 $F_j$是已经求出的,最后的问题就是如何快速求出 $max(A_k)$ 。 单调队列中的 元素 $j$的下一个元素就是要求的$max(A_k)$。 因为单调队列中$A_j$是递减的。  

      另外还有许多细节需要注意,看代码吧(

    代码

     1 #include<cstring>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<set>
     5 #define rep(i,a,b) for( int i = (a); i <= (b); ++i )
     6 #define per(i,a,b) for( int i = (a); i >= (b); --i )
     7 #define rd read()
     8 using namespace std;
     9 typedef long long ll;
    10 
    11 const int N = 2e5 + 1e4;
    12 
    13 int n, a[N], q[N];
    14 ll f[N], m;
    15 
    16 set<ll>st;
    17 
    18 int read() {
    19     int X = 0, p = 1; char c = getchar();
    20     for(; c > '9' || c < '0'; c = getchar() ) if( c == '-' ) p = -1;
    21     for(; c >= '0' && c <= '9'; c = getchar() ) X = X * 10 + c - '0';
    22     return X * p;
    23 }
    24 
    25 inline ll cmin( int A ,int B ) {
    26     return A < B ? A : B;
    27 }
    28 
    29 int main()
    30 {
    31     n = rd;
    32     scanf("%lld",&m);
    33     rep( i, 1, n ) a[i] = rd;
    34     f[0] = 0;
    35     int low = 0, l = 1, r = 0;
    36     ll sum = 0;
    37     set<ll>::iterator it;
    38     rep( i, 1, n ) {
    39         sum += a[i];
    40         while( sum > m ) sum -= a[++low]; // 求出最小的j使得连续一段和不超过m
    41         if( low >= i ) return printf("-1
    "), 0;
    42         while( l <= r && q[l] < low ) {//检验队首是否满足连续和不超过m
    43             if( l < r ) st.erase( f[q[l]] + a[q[l+1]]);//队列中删除被弹出的答案
    44             l++;
    45         }
    46         while( l <= r && a[q[r]] <= a[i] ) {//使队列递减
    47             if( l < r ) st.erase( f[q[r - 1]] + a[q[r]]);
    48             r--;
    49         }
    50         q[++r] = i;
    51         if( l < r ) st.insert( f[q[r - 1]] + a[q[r]]);//加入i,这样才能更新出可行的最优答案
    52         f[i] = f[low] + a[q[l]];
    53         if( st.size() ) {
    54             it = st.begin();
    55             f[i] = cmin( *it, f[i]);
    56         }
    57     }
    58     printf("%lld
    ", f[n]);
    59 }
    View Code
  • 相关阅读:
    mybatis中大于等于小于等于的写法
    RandomAccess接口
    ArrayList源码解析
    使用Docker搭建MySQL主从复制(一主一从)
    狂神Docker视频学习笔记(基础篇)
    【JQ】jQuery实现将div中滚动条滚动到指定位置的方法
    JAVA线程池的基本使用
    史上最全的Java技术体系思维导图,没有之一!
    springboot整合kafka
    spring cloud alibaba 分布式事务解决方案之seata-1.3.0
  • 原文地址:https://www.cnblogs.com/cychester/p/9480687.html
Copyright © 2011-2022 走看看