zoukankan      html  css  js  c++  java
  • POJ 3017 Cut the Sequence (单调队列优化DP)

    题意:

      给定含有n个元素的数列a,要求将其划分为若干个连续子序列,使得每个序列的元素之和小于等于m,问最小化所有序列中的最大元素之和为多少?(n<=105。例:n=8, m=17,8个数分别为2 2 2 | 8 1 8 |1 2,答案为12。)

    思路:

      想明白一个队列+一个set就能完美解决这个问题?

      首先DP的转移式子是:dp[i]=min( dp[j] +max[j+1, i]  ),且sum[i]-sum[j]<=m,j为枚举的断开处。暴力寻找一个合适的j的复杂度为O(n2)。那么问题就在于如何寻找这个合适的j。

      先假设j的范围是(low, i],而max是随着j的增大单调不减的,则max[k,i]>=max[p,i]且k<p是肯定的。那么如果出现max[k,i]=max[p,i]且k<p的话,j=k明显更佳,因为dp[k]<=dp[p]是肯定的!那么如果出现max[k,i]>max[p,i]且k<p的话,取哪个就难说了,也是因为dp[k]<=dp[p] 。

      根据转移式子知道,j肯定是不跟i同组的,这是根据dp[j]的定义来决定的。那么j有可能的取值为low,或者为k(a[k]>a[i]),那么单调队列就形成了,队列中保存下标,表示所有a[k]>a[i]且low<=k<=i。

      但是这队列有什么用?值val可能等于队列中任一元素u,而val=dp[u]+max[u+1,i]是没有什么规律的。直接将所有val装进set中,最小的set.begin()就是我们要找的答案了。如果维护单调队列时需要删除怎么办?直接将算出来的val在set中删除。

      注意:如果队列为空,即a[i]是a[low,i]中的最大值,那么最多只能取j=low。如果low一旦改变了,队头元素也有可能改变喔~因为队头算出来的val可能是根据更小的low算出来的。由于多个val相同的情况是存在的,所以用multiset。

     1 //#include <bits/stdc++.h>
     2 #include <iostream>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <cmath>
     6 #include <set>
     7 #include <deque>
     8 #include <map>
     9 #include <algorithm>
    10 #include <vector>
    11 #include <iostream>
    12 #define pii pair<int,int>
    13 #define back que[rear-1]
    14 #define INF 0x7f7f7f7f
    15 #define LL long long
    16 #define ULL unsigned long long
    17 using namespace std;
    18 const double PI  = acos(-1.0);
    19 const int N=100100;
    20 LL sum[N], has[N], dp[N];
    21 int a[N], que[N], top, rear, low;
    22 multiset<LL> sett;
    23 LL cal(int n,LL m)
    24 {
    25     for(int i=1; i<=n; i++)
    26     {
    27         if(a[i]>m)  return -1;
    28         while( sum[i]-sum[low]>m )  low++;
    29 
    30         //过期的只可能在top处
    31         while( top<rear && que[top]<=low )
    32             sett.erase( has[top++] );
    33 
    34         if(top<rear)
    35         {
    36              //队头的val有可能会改变,因为下限low提高了
    37             sett.erase( has[top] );
    38             has[top]=dp[low]+a[ que[top] ];
    39             sett.insert( has[top] );
    40         }
    41         //插入队列先
    42         while(top<rear && a[i]>a[ que[rear-1] ] )
    43             sett.erase( has[--rear] );
    44 
    45         que[rear]=i;    //只记下标
    46         LL val=0;
    47         if(top^rear)    val=dp[ que[rear-1] ]+a[i];
    48         else            val=dp[low]+a[i]; //top=rear时,a[i]就是最大的
    49         sett.insert( val );
    50         has[rear++]=val;
    51         dp[i]=*sett.begin();    //取最小
    52     }
    53     return dp[n];
    54 }
    55 
    56 int main()
    57 {
    58     //freopen("input.txt","r",stdin);
    59     int n;LL m;
    60     scanf("%d%lld", &n, &m);
    61     for(int i=1; i<=n; i++) //机器
    62     {
    63         scanf("%d",&a[i]);
    64         sum[i]=sum[i-1]+a[i];
    65     }
    66     printf("%lld
    ", cal(n,m));
    67     return 0;
    68 }
    AC代码
  • 相关阅读:
    php多态
    ssl certificate problem: self signed certificate in certificate chain
    test plugin
    open specific port on ubuntu
    junit vs testng
    jersey rest service
    toast master
    use curl to test java webservice
    update folder access
    elk
  • 原文地址:https://www.cnblogs.com/xcw0754/p/4864935.html
Copyright © 2011-2022 走看看