洛谷 P2885 题解
教练(校外)给我讲了这道题后觉得很有必要记录一下...
明显有个很明显的暴力(废话)
\(f_{i,j} = min(f_{i-1, j'} + c * |j - j'|) + (j - a_i) ^ 2\)
吸氧后能过(@我吃死酸辣粉 测试)
现在我们要进一步优化它
很明显这个绝对值可以拆成两个方程的 \(min\)
\(f_{i,j} = min(f_{i-1, j'} + c * (j - j')) + (j - a_i) ^ 2\)
\(f_{i,j} = min(f_{i-1, j'} + c * j - c * j') + (j - a_i) ^ 2\)
和
\(f_{i,j} = min(f_{i-1, j'} + c * (j' - j)) + (j - a_i) ^ 2\)
\(f_{i,j} = min(f_{i-1, j'} + c * j' - c * j) + (j - a_i) ^ 2\)
我们发现中间的 \(c * j\) 和取 min 没有关系,就把它放在外面
\(f_{i,j}=\begin{cases}min(f_{i-1, j'} - c * j') + (j - a_i) ^ 2 + c * j&j' \le j\\min(f_{i-1, j'} + c * j') + (j - a_i) ^ 2 - c * j&j \le j'\end{cases}\)
然后我们发现,前面的那一坨 \(min\) ,其实不需要第一篇题解的单调队列优化,只需要一边算一边取 \(min\)
大概就是一开始设 \(tot = \texttt{INF}\) ,然后遍历 \(j\) 从 \(1 \le j \le 100\) (或 \(100 \ge j \ge 1\)),每次都更新 \(tot\) 为 \(min(tot, f_{i-1, j} + c * j)\) ,然后注意因为树的高度只能增加不能减少,所以要注意在 \(j < h_i\) 时不要更新 \(f\) ,直接设为 \(\texttt{INF}\) 然后 continue ,但是 \(tot\) 仍要更新(我就踩了这个坑)。
注意方程的加减号(踩坑 * 2),先把方程捋明白了再写...
最后求的值就是 \(\min\limits_{i=a_{i}}^{100}f_{n,i}\)
代码:
// jaco2567 AK IOI
// #include <bits/stdc++.h>
#include <queue>
#include <stack>
#include <cmath>
#include <string>
#include <cstdio>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0, f = 1;
char k;
while (!isdigit(k = getchar())) if(k == '-') f = -1;
while (isdigit(k)) {
res = res * 10 + k - '0';
k = getchar();
}
return res * f;
}
const int NR = 1e5 + 5;
const int INF = 0x3f3f3f3f;
int n, a[NR], c, f[NR][105], tot;
int main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
memset(f, INF, sizeof(f));
scanf("%d%d", &n, &c);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= 100; i++) {
if (i >= a[1]) f[1][i] = (i - a[1]) * (i - a[1]);
else f[1][i] = INF;
}
for (int i = 2; i <= n; i++) {
tot = INF;
for (int j = 1; j <= 100; j++) {
tot = min(tot, f[i-1][j] - c * j);
if (j < a[i]) {
f[i][j] = INF;
continue ;
}
f[i][j] = min(f[i][j], tot + c * j + (j - a[i]) * (j - a[i]));
}
tot = INF;
for (int j = 100; j >= 1; j--) {
tot = min(tot, f[i-1][j] + c * j);
if (j < a[i]) {
f[i][j] = INF;
continue ;
}
f[i][j] = min(f[i][j], tot - c * j + (j - a[i]) * (j - a[i]));
// cout << i << ' ' << j << ' ' << f[i][j] << endl;
}
}
int ans = INF;
for (int i = 0; i <= 100; i++) {
ans = min(ans, f[n][i]);
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号