洛谷 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;
}