题意:
有一堆玩具可以压缩成 Ci 长度,放在容器中,同时一个容器内玩具的编号必须是连续的,每个容器所需要的费用题目中给了,求最小费用。
思路:
1. 网上推导的代码啰里啰嗦的,完全没有理解清楚不变量 i 的真谛,从而对于平方的展开添加了很多不必要的麻烦。
2. dp[i] 表示拿到第 i 件玩具时,所需要的最小费用。当 i 和前面的 x 件连着时,才有有递推式:dp[i] = dp[j] + (i-j-1 + Ci-Cj + L)2;
3. 对平方里面的因式进行必要的化简:S[i] = i + Ci; 于是 dp[i] = dp[j] + (Si - Sj + L - 1)2;
4. 再进行精简,令 m = Si + L - 1; dp[i] = dp[j] + (m - Sj)2 = dp[j] + m2 - 2*m*Sj + Sj2;
5. 利用斜率优化,此时有 X = Sj, Y = dp[j] + Sj2, a = 2*m; a 满足递增的特性,求 dp[i] 最小。根据下凸函数的特性,利用队列对其进行优化即可,O(N);
6. 要注意的一个边界是 :当 i 固定, j 可能取 0 时使结果最优,所以对于 0 要提前入队,这样得到总体最优解。
#include <iostream>#include <stdio.h>#include <algorithm>using namespace std;#define LL long long intconst int MAXN = 50010;int deq[MAXN];LL C[MAXN], S[MAXN], dp[MAXN];inline double slope(int i, int j){ return 1.0 * (dp[i] + S[i] * S[i] - dp[j] - S[j] * S[j]) / (S[i] - S[j]);}int main(){ int N, L; while (scanf("%d %d", &N, &L) != EOF) { C[0] = S[0] = 0; for (int i = 1; i <= N; ++i) { scanf("%lld", &C[i]); C[i] += C[i-1], S[i] = i + C[i]; } int s = 0, e = 0; dp[0] = deq[s] = 0; for (int i = 1; i <= N; ++i) { LL m = S[i] - L - 1; while (s < e && slope(deq[s+1], deq[s]) <= 2 * m) ++s; int j = deq[s]; dp[i] = dp[j] + (m - S[j]) * (m - S[j]); while (s < e && slope(deq[e], deq[e-1]) >= slope(i, deq[e])) --e; deq[++e] = i; } printf("%lld\n", dp[N]); } return 0;}