第一次写斜率优化 DP,跟着 这篇题解 学的,感觉讲的很清楚。
大致思路就是把暴力 DP 式子拆开来,分常数项和转移项合并,然后可以得到一个看上去非常小清新的式子。
然后可以推出对于 $j_1 \lt j_2 \lt i$,满足 $j_1$ 转移比 $j_2$ 转移优的条件(一个不等式)。
接着可以使用这个不等式忽略掉对答案一定没有贡献的项,这可以采用单调队列维护凸包。
然后在凸包中斜率是单调的,所以可以二分出第一个 $\geq 2 h_i$ 的点的位置,进行转移。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15;
int n, h[N];
long long C;
int q[N], st, ed;
long long dp[N];
double x(int p) { return h[p]; }
double y(int p) { return dp[p] + h[p] * 1ll * h[p]; }
double slope(int a, int b) {
return (y(b) - y(a)) * 1.00 / (x(b) - x(a));
}
int main() {
scanf("%d%lld", &n, &C);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
st = ed = 1, q[ed] = 1;
for (int i = 2; i <= n; i++) {
while (st < ed && slope(q[st], q[st + 1]) <= 2 * h[i]) st++;
int l = st, r = ed - 1;
while (l < r) {
int mid = l + r >> 1;
if (slope(q[mid], q[mid + 1]) < 2 * h[i]) l = mid + 1;
else r = mid;
}
int j = q[l];
dp[i] = dp[j] + (h[i] - h[j]) * 1ll * (h[i] - h[j]) + C;
while (st < ed && slope(q[ed - 1], q[ed]) >= slope(q[ed], i)) ed--;
q[++ed] = i;
}
printf("%lld\n", dp[n]);
return 0;
}
但在本题中,由于保证 $h_i$ 单调递增,所以可以直接拿队首转移。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15;
int n, h[N];
long long C;
int q[N], st, ed;
long long dp[N];
double x(int p) { return h[p]; }
double y(int p) { return dp[p] + h[p] * 1ll * h[p]; }
double slope(int a, int b) {
return (y(b) - y(a)) * 1.00 / (x(b) - x(a));
}
int main() {
scanf("%d%lld", &n, &C);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
st = ed = 1, q[ed] = 1;
for (int i = 2; i <= n; i++) {
while (st < ed && slope(q[st], q[st + 1]) <= 2 * h[i]) st++;
int j = q[st];
dp[i] = dp[j] + (h[i] - h[j]) * 1ll * (h[i] - h[j]) + C;
while (st < ed && slope(q[ed - 1], q[ed]) >= slope(q[ed], i)) ed--;
q[++ed] = i;
}
printf("%lld\n", dp[n]);
return 0;
}