Pre
题目链接。
这才是属于斜率优化的模板题,转移方程简单,公式转化也简单。
Statement
有 N 个石头,编号为 1,2,…,N,第 i 个高为 hi。保证 h 严格单调递增。
有一只青蛙在第一个石头上,它可以跳到石头编号为 i+1,i+2,…,N。当他跳到编号 j 石头时的花费是
(hi−hj)2+C。求跳到编号为 N 石头的最小花费。
Solution
首先转移方程可以直接秒掉,定义 dpi 表示落到第 i 个石头上的最小代价,有:
dpi=j<imindpj+(hi−hj)2+C
然后参数分离,有:
dpi=j<imin(dpj+hi2−2hihj+hj2+C)=hi2+C+j<imin(dpj+hj2−2hihj)
令 k←−2hi,Fi←dpi+hi2,则有
dpi=2+C+j<imin(Fj+k×hj)
考虑枚举 j(j<i),不妨设 j1,j2 (j1<j2<i) 并且 j1 不劣于 j2,则有不等式:
Fj1+k×hj1k×(hj1−hj2)≤Fj2+k×hj2≤Fj2−Fj1
由于 k 为负数,移项后则有:
−2hihj1−hj2Fj1−Fj2≤hj1−hj2Fj2−Fj1≥2hi
至此,斜率就求出来了,斜率求最接近 2hi 的即可,解释如下。
Simulation
定义 slope(i,j) 表示 i,j 两点的斜率,实现函数如下(与公式相关联):
1 2 3
| inline double X(int i) { return (double)f[i] + (double)h[i] * h[i]; } inline double Y(int i) { return (double)h[i]; } inline double slope(int i, int j) { return (X(i) - X(j)) / (Y(i) - Y(j)); }
|
若有三个点 i,j,k (i<j< k),并且最优的决策点为 j,根据上述公式,有:
slope(i,j)≤2hislope(j,k)≥2hi⇒slope(i,j)≤2hi≤slope(j,k)
由此可知每个 slope 的值应当尽可能的小,然后就维护出来一个凸包:
处理出来了二分即可,但是本题很显然是单调的,因此使用单调队列即可,注意到不像一般的队列一样,队列当中应当至少有一个元素,因为至少三个元素才可以进项比较。
Code
1 2 3 4 5 6 7 8 9
| que[++ tt] = 1; for (int i = 2; i <= n; i ++) { while (hh < tt && slope(que[hh], que[hh + 1]) <= 2.0 * h[i]) hh ++; f[i] = f[que[hh]] + (h[i] - h[que[hh]]) * (h[i] - h[que[hh]]) + C; while (hh < tt && slope(que[tt - 1], que[tt]) >= slope(que[tt], i)) tt --; que[++ tt] = i; } cout << f[n] << '\n';
|