Description
Input
Output
Sample Input
4
3 1 0 2
Sample Output
5
4
3
1
Data Constraint
思路
不会在护甲尚未变为 0 时再次叠加护甲,如果要叠加护甲,一定是在一开始一起叠加不会跟劣。
于是我们可以设一个短篇 dp,设 f[i][j]表示当前停留在位置 i 并且没有法力值,当前总共消耗的护甲值为 j,
1~i-1 的伤害和已经统计完毕的最小伤害和,那么显然有 f[i][j]+c[i][p] →f[i+p][j+p]以及 f[i][j]+a[i]→f[i+1][j] ,
c[i][p]表示在 i 处叠加一个 p 的护甲 i~i+p-1 造成的伤害和,显然有 c[i][p]=c[i+1][p-1]+max(a[i]-p,0),这样
可以 O(n^3)做了。
现在考虑如何优化这个 dp,观察转移式子可以发现每次都是 f[i][j]转移到 f[i+p][j+p],转移到的状态的第一
维与第二维的差值是不会变的,再观察一个性质,可以发现 c[i-p][j+p]>=c[i]j,按照 i-j 给状态分组,
那么除掉第二个转移,转移都在同一组中进行,我们观察一下转移的系数,现在考虑一下 f[i+d][i]和 f[j+d][j]
对某个 f[d+z][z]的转移(i>j)孰优孰劣,可以看到 f[i+d][i]的转移为 f[i+d][i]+c[i+d][z-i]→f[d+z][z]以及
f[j+d][j]+c[j+d][z-j] →f[d+z][z],可以发现随着 z 的增大,c[j+d][z-j]-c[i+d][z-i]的值在逐渐减少,简单来说,
就是也就是对于比较小的 z,由 f[i+d][i]转移过来更优,对于较大的 z,由 f[j+d][j]转移过来更优,因此我们可
以维护一个单调栈来维护最优决策区间,更新单调栈时,需要二分一次决策单调点,因此复杂度是 O(n^2
log n)
代码
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define G(i, a, b) for (int i = a; i >= b; i --)
#define mem(a, b) memset(a, b, sizeof a)
#define mn(a, b) ((a) = min(a, b))
const int N = 4010;
using namespace std;
int n, TOT, sum, v, ans, G[N][N];
int a[2 * N], ct[N], C[N][N], Len[N], g[N][N], f[2 * N][N];
int main() {
freopen("griffin.in", "r", stdin);
freopen("griffin.out", "w", stdout);
scanf("%d", &n), Len[0] = 1, g[0][1] = 1e9;
F(i, 1, n) scanf("%d", &a[i]), Len[i] = 1, g[i][1] = 1e9;
F(i, 1, n) {
mem(ct, 0);
F(j, i, n)
if (a[j] + j - i <= n) ct[a[j] + j - i] ++;
C[i][0] = a[i], TOT = - ct[0], sum = 0;
F(j, 1, n) {
if (i + j - 1 <= n) TOT ++, sum += a[i + j - 1];
sum -= TOT, TOT -= ct[j], C[i][j] = sum;
}
}
mem(f, 7), f[0][0] = 0;
F(i, 0, n - 1) {
F(d, 0, 2 * n - i - 1)
mn(f[i + (d + 1)][i], f[i + d][i] + a[i + d + 1]);
if (i > 0) {
F(d, 0, n - i - 1) {
v = G[d][Len[d]];
if (f[i + d][i] >= f[v + d][v]) continue;
ans = i;
for (int l = i + 1, r = n; l <= r; ) {
int m = l + r >> 1;
if (f[i + d][i] + C[i + d + 1][m - i] < f[v + d][v] + C[v + d + 1][m - v])
ans = m, l = m + 1;
else
r = m - 1;
}
G[d][++ Len[d]] = i, g[d][Len[d]] = ans;
}
}
F(d, 0, 2 * n - i - 1) {
while (d < n && i + 1 > g[d][Len[d]]) Len[d] --;
v = d >= n ? 0 : G[d][Len[d]];
mn(f[(i + 1) + d][i + 1], f[v + d][v] + (v + d + 1 > n ? 0 : C[v + d + 1][i + 1 - v]));
}
}
F(i, 1, n) {
ans = 1e15;
F(k, n, n + n - 1)
ans = min(ans, f[k][i]);
printf("%d\n", ans);
}
}