题意:
有一堆玩具可以压缩成 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 int
const
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;
}