题目含义
给出一堆数字,要求分成几个区间,并且每个区间的和不大于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]); }