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]);
    }
  • 相关阅读:
    PAT Advanced 1067 Sort with Swap(0, i) (25分)
    PAT Advanced 1048 Find Coins (25分)
    PAT Advanced 1060 Are They Equal (25分)
    PAT Advanced 1088 Rational Arithmetic (20分)
    PAT Advanced 1032 Sharing (25分)
    Linux的at命令
    Sublime Text3使用指南
    IntelliJ IDEA创建第一个Groovy工程
    Sublime Text3 安装ftp插件
    Sublime Text3配置Groovy运行环境
  • 原文地址:https://www.cnblogs.com/helman/p/11252521.html
Copyright © 2011-2022 走看看