CF1077F2 Pictures with Kittens (hard version)
题解里好像没有和我做法一样的 (指状态不一样),那我就来发一篇,定义状态fi,jf_{i,j}表示当前选了ii个数字,在第jj个数字时的和的最大值
那么很容易可以想出转移方程
fi,j=max0u<j1ix,0jnfi1,u+ajf_{i,j}=\max\limits_{0\le u<j}\limits^{1\le i \le x,0\le j\le n}f_{i-1,u}+a_j

很显然直接O(n3)O(n^3)转移是不行的,只能过24个点,别问我为什么知道,那么就可以考虑优化,去掉一维,仔细观察发现似乎可以用单调队列优化第三维,那么就可以用O(n2)O(n^2)成功转移了
代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 5005;
typedef long long ll;

ll f[N][N], a[N];
int q[N];

int main()
{
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
f[i][j] = -1e18;
int n, k, m;
scanf("%d%d%d", &n, &k, &m);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
f[0][0] = 0;//初始状态
for (int i = 1; i <= m; i++)
{
int front = 0, tail = -1;
for (int j = 0; j <= n; j++)
{
if (front <= tail && q[front] < j - k)
front++;
if (front <= tail)
f[i][j] = f[i - 1][q[front]] + a[j];
while (front <= tail && f[i - 1][j] >= f[i - 1][q[tail]])
tail--;
q[++tail] = j;//单调队列优化
//for (int u = 0; u < j; u++)
//{
// if (j - u <= k)
// f[i][j] = max(f[i][j], f[i - 1][u] + a[j]);
//}暴力转移的代码
}
}
ll ans = -1;
for (int i = n - k + 1; i <= n; i++)
ans = max(ans, f[m][i]);
printf("%lld", ans);
}

这里还可以有一个小优化,就是用滚动数组,实测加上滚动数组过后时间从7.5s2.97s7.5s\to 2.97s