题解:P10450 & 斜率优化dp学习笔记
Expert_Dream · · 算法·理论
本题有十分简单的做法,但可以作为斜率优化 Dp 的很好练手题。
斜率优化
在一些 dp 转移方程简化后,可以转换为坐标系中的一个斜率表示。
dp 的转移方程模型
最大值和最小值优化方式相反。
然而
我们发现
暂且忽略
转换成
对于一条一次函数
也就是说已知斜率,求一些过点
看下面的图,首先例如这三个点,红蓝绿。
我们发现在这个斜率下,红与 Y 轴相切的坐标最低,所以答案是红。
在看这个图,在这样的斜率下,绿与 Y 轴相切的坐标最低,所以答案是绿。
再看最后一个图,斜率更大时,绿与 Y 轴相切的坐标最低,答案依然是绿。
从而得出,无论怎样的斜率下,蓝必定都不是最优解。
所以可以忽略蓝这种情况。
而维护这样的点就与凸包有关。
凸包指的是一个坐标系中一些点,然后用一条边来“包”住他们。会变成一个凸多边形。例如下图,实线是 不合法 的情况,直观来看,他就是一个多边形的边“凹”了进去。所以此时需要把实线改成虚线。
而这些点,是我们之前 dp 已经计算出来的点,那么就可以维护进一个数据结构里。例如 HDU-3507 是一道典型的斜率优化 dp 题目。这道题中,我们可以保证斜率 大于
那么。
回看上面典型的式子改成取
一般来说,会根据式子的意义和手动画图,来推断较优解的方向,来判断上凸壳或者下凸壳。
然而通过上述规则,维护了多个点的下凸包就如下图(来自 oiwiki)。
本题
易得转移方程
这种斜率方程与典型的有一点点区别,通过观察,易得:若
题意转换:求其中两点之间的斜率最大值,并且两点之间的横坐标差值有限制。
于是考虑斜率优化 dp 转移。由于
还有,本题有精度问题,最好是在
代码
int n,k;
int a[N];
double s[N];
int q[N],h,t;
double sol(int x,int y){
return (s[x]-s[y])/(x-y);
}
void solve( ){
cin >> n >> k;
For(i,1,n) cin >> a[i];
For(i,1,n) s[i] = s[i-1] + a[i];
For(i,1,n) s[i] *= 1000;
h = 1;
double ans = 0;
For(i,k,n){
while(t>=h && sol(i,q[h]) <= sol(i,q[h+1]))++ h;
ans = max(ans,sol(i,q[h]));
while(t>=h && sol(q[t],q[t-1]) >= sol(i-k+1,q[t])) --t;
q[++t] = i-k+1;
}cout <<(int)ans;
}