小学二年级都能看懂的 动态规划-斜率优化学习笔记
2022.10.18 update:修改了题面错误,并增加了代码
本文主要参考oi-wiki 斜率优化
引入
有
个玩具,第 个玩具价值为 。要求将这 个玩具排成一排,分成若干段。对于一段 ,它的代价为 。其中 是一个常量,求分段的最小代价。
很显然,这道题可以用简单的dp实现
设
即使使用前缀和来维护
斜率优化
令
接下来就是斜率优化的具体步骤了:
1.转换问题
第一步:简化。把最外圈的
第二步:展开,把所有不只含
第三步:(应该是最难理解的一步)
在每次处理
原方程就能表示成:
这非常像(就是)一个一次函数表达式
注意到:
由于
所以原题就可以转变成:找到一组
2.处理
但是做完了这个之后,感觉问题不仅没有变简单,反而更复杂了更难求了……
别急,毕竟是优化,肯定是要比暴力要难处理一点的
考虑如何让直线截距最小。显然,将直线一直往上移,碰到的第一个点就能让直线截距最小
观察下面一张图:
不难发现,直线可能碰到的第一个点,在所有
很显然,
更具体地讲,我们每加入一个点
维护好了下凸包,怎么统计答案呢?
假设直线第一个碰到的点为
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e4+5e4; int f[N]; int c[N],l; int pre[N],s[N]; double stk[N]; int x[N],y[N]; int top,bot=1;//bot如果初始化为0的话,i=1时有概率出锅 int n; double cack(int i,int j){ return (y[i]-y[j])/double(x[i]-x[j]); } int cacans(int i,int j){ return f[j-1]+(pre[i]-pre[j-1]+i-j-l)*(pre[i]-pre[j-1]+i-j-l); } signed main(){ cin>>n>>l; for(int i=1;i<=n;i++) scanf("%lld",c+i); for(int i=1;i<=n;i++) pre[i]=pre[i-1]+c[i],s[i]=pre[i]+i; for(int i=1;i<=n;i++){ x[i]=s[i-1],y[i]=f[i-1]+s[i-1]*s[i-1]+2*s[i-1]*(l-1); while(bot<top&&cack(stk[top],stk[top-1])>cack(i,stk[top-1])) top--; //记得保证栈内最少有一个元素(即bot<top) stk[++top]=i; while(bot<top&&cacans(i,stk[bot])>=cacans(i,stk[bot+1])) bot++; f[i]=cacans(i,stk[bot]); } cout<<f[n]; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】