题目大意
题意都这么明确了还要这个干什么。
存在 \(n\) 个点,每个点有一个属性 \(h_i\),\(h_i\) 单增,从点 \(i\) 移动到点 \(j(j>i)\) 的代价是 \((h_i-h_j)^2+C\),其中 \(C\) 是给定的常数,求从点 \(1\) 移动到点 \(n\) 的最小代价。
思路分析
斜率优化 DP 板题。
设 \(f_i\) 表示从点 \(1\) 移动到点 \(i\) 的最小代价,容易列出状态转移方程:
\[f_i=\min_{j<i}(f_j+(h_i-h_j)^2+C) \]直接转移是 \(O(n^2)\) 的,考虑斜率优化:
\[\begin{aligned}f_i&=f_j+h_i^2-2h_ih_j+h_j^2+C\\(f_i-h_i^2)&=(f_j+h_j^2+C)-(2h_i)(h_j)\end{aligned} \]将第 \(i\) 个状态视为平面上的点 \((h_i,f_i+h_i^2+C)\),问题转化为求斜率固定的直线的截距的最小值,考虑到 \(h_i\) 单增,故点的横坐标单增,斜率也单增。使用单调队列维护下凸壳即可。
代码
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=200200;
#define int long long
#define y(i) (i[f]+i[h]*i[h]+C)
#define x(i) (i[h])
#define k(i) (2*i[h])
int n,C,hh,tt;
int q[N],f[N],h[N];
double slope(int i,int j){
return 1.0*(y(i)-y(j))/(x(i)-x(j));
}
signed main(){
scanf("%lld%lld",&n,&C);
for(int i=1;i<=n;i++) scanf("%lld",&i[h]);
hh=1;tt=1;hh[q]=1;
for(int i=2;i<=n;i++){
while(hh<tt&&slope(hh[q],(hh+1)[q])<k(i)) hh++;
i[f]=hh[q][f]+(i[h]-hh[q][h])*(i[h]-hh[q][h])+C;
while(hh<tt&&slope(i,(tt-1)[q])<slope((tt-1)[q],tt[q])) tt--;
(++tt)[q]=i;
}
cout<<n[f]<<'\n';
return 0;
}
标签:单增,int,题解,Frog,斜率,include,define
From: https://www.cnblogs.com/TKXZ133/p/17567915.html