【题解】CF#1012 C-Hill
感觉这题的状态还是比较明显的。设置状态 \(f[i][j][0/1]\) 表示dp到第 \(i\) 个位置,前面(包括这里)已经出现了 \(j\) 个山峰,当前位置是不是山峰即可 dp。这样的状态有一个很优秀的性质:我们注意到在最后的答案当中,我们一定不会去修改山峰的高度(这样做显然毫无意义)。那么这样dp我们可以通过分类讨论直接计算出使一个节点成为山峰的代价。
如:降低一个位置使得前面与后面均为山峰,降低到小于两者的高度。降低一个位置使得后一个位置为山峰,降低一个位置使得前一个位置为山峰,都只要降低到为山峰的位置更低即可。复杂度很高,但codeforces经常都是玄学复杂度……( • ̀ω•́ )✧
#include <bits/stdc++.h> using namespace std; #define maxn 100000 #define int long long #define INF 999999999999LL int n, K, a[maxn], f[3][maxn][2]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int Check(int x, int y, int z) { int t = min(y, z); return x >= t ? x - t + 1 : 0; } void Down(int &x, int y) { x = x < y ? x : y; } signed main() { n = read(), K = (n >> 1) + 1; if(!(n & 1)) K -= 1; for(int i = 1; i <= n; i ++) a[i] = read(); int fore = 0, pre = 1, now = 2; for(int i = 0; i <= 2; i ++) for(int j = 0; j < maxn; j ++) f[i][j][0] = f[i][j][1] = INF; f[fore][0][0] = f[pre][0][0] = 0; for(int i = 1; i <= n; i ++) { int t = (i >> 1) + 1; for(int j = 0; j < maxn; j ++) f[now][j][0] = f[now][j][1] = INF; for(int j = 0; j <= i; j ++) { int t1, t2, t3; if(i > 2) t1 = a[i - 2], t2 = a[i - 1], t3 = a[i]; else if(i > 1) t1 = -INF, t2 = a[i - 1], t3 = a[i]; else t1 = -INF, t2 = -INF, t3 = a[i]; Down(f[now][j][1], f[fore][j - 1][1] + Check(t2, t1, t3)); Down(f[now][j][1], f[fore][j - 1][0] + Check(t2, t3, INF)); Down(f[now][j][0], f[pre][j][1] + Check(t3, t2, INF)); Down(f[now][j][0], f[pre][j][0]); } int tem = fore; fore = pre, pre = now, now = tem; } for(int i = 1; i <= K; i ++) printf("%I64d ", min(f[pre][i][0], f[pre][i][1])); puts(""); return 0; }