CF1077F2 Pictures with Kittens (hard version)
题解里好像没有和我做法一样的 (指状态不一样),那我就来发一篇,定义状态fi,j表示当前选了i个数字,在第j个数字时的和的最大值
那么很容易可以想出转移方程
fi,j=0≤u<jmax1≤i≤x,0≤j≤nfi−1,u+aj
很显然直接O(n3)转移是不行的,只能过24个点,别问我为什么知道,那么就可以考虑优化,去掉一维,仔细观察发现似乎可以用单调队列优化第三维,那么就可以用O(n2)成功转移了
代码
#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.5s→2.97s