zoukankan      html  css  js  c++  java
  • 单调队列,dp——POJ

    题目含义

    给出一堆数字,要求分成几个区间,并且每个区间的和不大于m,并求每个区间的最大值之和最小

    题目分析

    动态转移方程是dp[i]=dp[j]+max(a[j+1],a[j+2],...,a[i])

    可以理解为,一个长的数列的值等于一个短的数列的值加上剩下的数的最大值

    但是,sum[i]-sum[j]必须小于m,才能把max单独划出来

    即便如此,我们也不知道哪一个j才是答案所划分的那个j

    ——————————————————————

    首先确定的是,a[j+1]到a[i]的和必须要求小于m

    那么假定a[k]是里面的最大值,如果把j+1,...,k-1分到左边去,那么右边的值不变,左边的值可能增加,不满足求最小值的要求

    那么把j+1,...,k移到左边去后,就变成了dp[k]+max(a[k+1],..,a[i])右边是仅次于a[k]的最大值

    一直分,最后就是dp[i-1]+a[i]

    也就是说右边的取值遍历了从a[k]到a[i]的这样一个单调递减数列

    所以我们可以用单调队列来求取

    题目代码

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    typedef long long LL;
    const int maxn=1e5+7;
    LL m,dp[maxn],que[maxn],a[maxn],sum[maxn];
    int n;
    int main(){
        scanf("%d%I64d",&n,&m);
        int flag=1;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
            if(a[i]>m)flag=0;
        }
        if(!flag){
            printf("-1
    ");
            return 0;
        }
        int head=1,tail=1,t=0;
        que[head]=1;
        dp[1]=a[1];
        for(int i=2;i<=n;i++){
            while(t<i&&sum[i]-sum[t]>m)t++;
            while(head<=tail&&a[que[tail]]<=a[i])tail--;
            que[++tail]=i;
            while(head<=tail&&sum[i]-sum[que[head]-1]>m)head++;
            dp[i]=dp[t]+a[que[head]];
            for(int j=head;j<tail;j++)
                dp[i]=min(dp[i],dp[que[j]]+a[que[j+1]]);
        }
        printf("%lld
    ",dp[n]);
    }
  • 相关阅读:
    [洛谷P2463][SDOI2008]Sandy的卡片
    后缀数组
    周记【距gdoi:110天】
    hdu3068最长回文(Manacher算法)
    二分图的一些题目合集
    周记【距gdoi:117天】
    二分图、网络流模版总结
    2014end
    周记【距gdoi:126天】
    2-sat
  • 原文地址:https://www.cnblogs.com/helman/p/11252521.html
Copyright © 2011-2022 走看看