题意:一个长度为
一道很明显的 dp 题。
状态:
很容易可以得到状态转移方程:
状态的时间复杂度为
容易发现,转移方程保证
意思就是,假设
此时很明显取最前面的
所以如何找最大的 multiset 维护
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
#include<ctime>
#define INF 1e9
using namespace std;
const int maxn=1e5+101;
long long n,m;
long long a[maxn],f[maxn],sum[maxn];
long long q[maxn],k,head,tail;//单调队列,维护最大a[i]的下标
multiset<long long> s;//维护f[j]+a[k](满足a[k]为[j+1,i]最大)
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
if(a[i]>m)
{
printf("-1");
return 0;
}
sum[i]=sum[i-1]+a[i];
}
head=1;
tail=0;
k=0;
for(int i=1;i<=n;i++)
{
while(sum[i]-sum[k]>m)
k++;//保证[k,i]和小于m
while(head<=tail&&q[head]<=k)//删除不合法决策
{
if(head<tail)
s.erase(a[q[head+1]]+f[q[head]]);
head++;
}
while(head<=tail&&a[q[tail]]<=a[i])//维护单调性
{
if(head<tail)
s.erase(a[q[tail]]+f[q[tail-1]]);
tail--;
}
q[++tail]=i;
if(head<tail)
s.insert(a[i]+f[q[tail-1]]);//每访问一次i,添加相应决策
f[i]=f[k]+a[q[head]];//最大值a[q[head]]对应的最优决策
if(head<tail)
f[i]=min(f[i],*s.begin());//其他决策
}
printf("%lld",f[n]);
return 0;
}