【题目链接】
【思路要点】
- 状态\(F_{i}\)表示把前\(i\)个玩具装箱的最小费用,\(S_{i}\)为\(C_{i}\)的前缀和。则有以下转移方程:$$F_{i}=min_{j=1}^{i}\{F_{j-1}+(i-j+S_{i}-S_{j-1}-L)^2\}$$
- 令\(A_{i}=i+S_{i}-L\),\(B_{j}=j+S_{j-1}\),用\(F_{j,i}\)表示前\(i\)个玩具决策\(j\)所需的费用,则有:
$$F_{j,i}=F_{j-1}+(A_{i}-B_{j})^2$$
$$F_{j,i}=F_{j-1}+B_{j}^2-2*B_{j}*A{i}+A_{i}^2$$- 考虑决策\(j\)和\(k\),满足\(k>j\)且\(F_{k,i}≤F_{j,i}\)的充要条件:
$$F_{k-1}+B_{k}^2-2*B_{k}*A{i}+A_{i}^2≤F_{j-1}+B_{j}^2-2*B_{j}*A{i}+A_{i}^2$$
$$\Leftrightarrow \frac{(F_{k-1}+B_{k}^2)-(F_{j-1}+B_{j}^2)}{B_{k}-B_{j}}≤2*A_{i}$$- 再令\(C_{i}=F_{i-1}+B_{i}^2\),上式即为
$$\frac{C_{k}-C_{j}}{B_{k}-B_{j}}≤2*A_{i}$$- \(A\)、\(B\)数组均递增,斜率优化DP即可。
- 时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 50005 template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } int n, l, r, q[MAXN]; long double L, Sum[MAXN], F[MAXN], G[MAXN]; int main() { read(n), read(L); L++; for (int i = 1; i <= n; i++) { read(Sum[i]); Sum[i] += Sum[i - 1] + 1; } l = 0, r = 0; q[0] = 0; for (int i = 1; i <= n; i++) { while (l < r && 2 * (Sum[i] - L) * (Sum[q[l + 1]] - Sum[q[l]]) >= (G[q[l + 1]] - G[q[l]])) l++; F[i] = F[q[l]] + (Sum[i] - L - Sum[q[l]]) * (Sum[i] - L - Sum[q[l]]); G[i] = F[i] + Sum[i] * Sum[i]; while (l < r && (G[i] - G[q[r]]) * (Sum[q[r]] - Sum[q[r - 1]]) <= (G[q[r]] - G[q[r - 1]]) * (Sum[i] - Sum[q[r]])) r--; q[++r] = i; } printf("%.0Lf\n", F[n]); return 0; }