斜率优化
斜率优化
dp回顾
对于所有的方程都需要枚举
维护出前缀的最值即可 维护出前缀的最值即可 维护出前缀的最值即可
特殊: 是否存在单调性, 数组本身是单调的
而斜率优化可优化的dp类型就是第四种
P3195 [HNOI2008]玩具装箱
分析
设
对于
设
这个方程就满足上面所说的第4类方程,就可以用到斜率优化
斜率优化
需要先把方程推导出斜率的公式
-
自己设置两个决策点
和 并且 优于
代入这两个决策点到方程中,推导什么样条件满足 优于
-
拆开不等式,把不等式变成斜率的形式
设
,代入
-
显然我们可以把
看做 横坐标, 看做 纵坐标,那么右边的式子也就变成了一个斜率 ,也就是说 ,决策点 优于 -
在决策点 j 依次向右移动的过程中,只有保证单调递增,所有的 j 才有可能成为最优决策点
如果存在三个决策点 出现 的斜率大于 的斜率,那么 必然不可能成为最优决策点
也就是说,
当 在黄色点时, 此时无论 是多大, 都有可能成为最有决策点
而当 在蓝色点位置时,则 , 是不可能成为最有决策点的,因为无论什么情况, 都会比 优,所以直接可以把 删除,这样所得到的序列就是单调的了 -
寻找答案,只需要二分查找维护出来的斜率队列,找到最大的那个满足条件的斜率,这个斜率所对应的右端点即为最优决策点
当然,这里也可以直接用单调队列来维护决策点队列
如果 和队首两个点的斜率 则删除队首
对于队尾两个点的斜率大于当前 和队尾的斜率,则删除队尾
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e4+10;
int n,head,tail;
ll a[maxn],sum[maxn],F[maxn],L,G[maxn],dp[maxn];
int dq[maxn];
double K(int j1,int j2){
return (double)(dp[j1]+G[j1]-dp[j2]-G[j2])/(F[j1]-F[j2]);
}
int main(){
/*2023.4.30 hewanying P3195 [HNOI2008]玩具装箱 斜率优化dp*/
scanf("%d%lld",&n,&L);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
F[i]=sum[i]+i;
G[i]=(F[i]+1+L)*(F[i]+1+L);
}
G[0]=(L+1)*(L+1); //把G[0]当作原点,可以不用判断队伍中只有一个元素的情况
for(int i=1;i<=n;i++){
while(head<tail&&K(dq[head],dq[head+1])<2*F[i]) head++;
dp[i]=dp[dq[head]]+(F[i]-F[dq[head]]-1-L)*(F[i]-F[dq[head]]-1-L);
while(head<tail&&K(dq[tail-1],dq[tail])>K(dq[tail],i)) tail--;
dq[++tail]=i;
}
printf("%lld\n",dp[n]);
return 0;
}
总结
- 斜率优化的题目一般代码都比较短,
的dp都比较好推,难就难在推公式的过程 - 在dp中可以常常设一些函数来表示固定的值,从而可以简化dp的式子,防止出现错误,如例题中的
- 注意初始化和是否要使用long long
本文作者:H_W_Y
本文链接:https://www.cnblogs.com/H-W-Y/p/17365781.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步