题目大意:求一个数字序列的划分,每一个划分块的和不大于M,求出所有划分块最大值的最小值。
DP方程:dp[i] = min{dp[j]+max(a[j+1],a[i])}
用一个单调队列存储所有可行的决策,这个单调队列里面存储条件是队列范围的所有元素和不大于M,所以队列元素的个数就是每个点的决策个数。
更详细的题解可以看一下别人的blog
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 100020;
const LL INF =(LL)1e19;
LL n,m;
LL dp[MAXN];
int q[MAXN],front,rear;
LL sum[MAXN];
LL a[MAXN];
void solve()
{
dp[0]=0;
for(int i=1;i<=n;i++)dp[i]=INF;
front=rear=0;
int st=0;
int tar;
for(int i=1;i<=n;i++)
{
while(rear>front&&a[i]>=a[q[rear-1]])rear--;
q[rear++]=i;
while(sum[i]-sum[st]>m)st++;
while(q[front]<=st)front++;
dp[i]=dp[st]+a[q[front]];
for(int j=front;j<rear-1;j++)
{
dp[i]=min(dp[i],dp[q[j]]+a[q[j+1]]);
}
}
printf("%lld\n",dp[n]);
}
int main()
{
bool fd=false;
sum[0]=0;
scanf("%lld%lld\n",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
if(a[i]>m)
{
fd=true;
}
}
if(fd)
{
printf("-1\n");
}
else
{
solve();
}
return 0;
}