那么可以得到如下转移方程:
$f[i][j] = min(f[i][j], f[k][j-1]+g[k+1][i])
那么考虑怎么求$g[i][j]$。
根据定义,一段区间的空余空间一定是用区间中的最大值作为网的大小,减去其他数字得到的和。即$g[i][j]=maxn*(j-i+1)-sum(i,j)$,其中区间和可以用前缀和$O(1)$处理。
由于我们的$dp$已经是$O(n^3)$了,所以我们考虑快速(最好是$O(1)$)求出区间最大值。
首先想到的是线段树,但有一只$log$,太慢了。然后发现没有修改操作,可以用$st$表$O(1)$查询搞一搞(没写过不知道效果怎么样)。
但实际上有一种更简洁的方式,由于我们枚举断点的时候会从$i-1$向下逐一枚举,那么我们可以在枚举断点的时候同时更新最大值。
```cpp
#include <bits/stdc++.h>
#define MAX 405
using namespace std;
int n, m;
int a[MAX], f[MAX][MAX], s[MAX];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
s[i] = s[i-1]+a[i];
}
m++;
memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= min(m, i); ++j) {
int mx = a[i];
for (int k = i-1; k >= 0; --k) {
f[i][j] = min(f[i][j], f[k][j-1]+mx*(i-k)-(s[i]-s[k]));
mx = max(mx, a[k]);
}
}
}
int ans = 0x3f3f3f3f;
for (int i = 0; i <= m; ++i) {
ans = min(ans, f[n][i]);
}
cout << ans << endl;
return 0;
}
```