斜率优化DP学习笔记
RT,最近练习一些斜率优化的DP,也发现了不少的问题,下面进行一下小小的记录与知识的回顾与理解
介绍
首先,斜率优化是一种对DP进行优化的好东西,大多数的时候,它的使用可以把
原理
它的原理,基于数形结合的思想。有时候,我们会遇到一些DP题目,而这些题目有一个特点,就是它的决策从前面已经求出来DP值的状态(即前驱状态)中进行转移(其实这句话和废话一样)然后,我们发现它们的表达式是这样的形式:
其中
为了知道我们选取哪一个状态进行计算结果最优,我们可以做如下的作差比较,考虑前驱状态
如果
不妨令
这时,我们类比解析几何里两点斜率的定义,可以这样进行点的创建:
上述式子表示的就是
当然上述式子比较狭义,广义的来说是可以通过各种手段来做到这一步就可以:
其中
应用
下面贴几道例题,当然由于我非常弱,所以持续更新
BZOJ 1010: [HNOI2008]玩具装箱toy
经典题,没有什么好说的,采用上文所述的广义的方法即可,丑陋的代码如下
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define maxn 50005
#define LL long long int
#define A (LL)(2*i+2*pre[i]-2*l-2)
#define EFF (LL)(i-op.idx+pre[i]-pre[op.idx]-l-1)*(i-op.idx+pre[i]-pre[op.idx]-l-1)
#define set_y(k) (LL)(dp[k]+(k+pre[k])*(k+pre[k]))
#define set_x(k) (LL)(k+pre[k])
#define slope(i,j) (double)(i.y-j.y)/(i.x-j.x)
using namespace std;
struct point{
LL x,y,idx;
void read(LL x,LL y,LL idx){
this->x=x,this->y=y,this->idx=idx;
}
}stack[maxn];
LL n,l,top=0,now=0;
LL len[maxn],dp[maxn],pre[maxn];
void DP(){
dp[0]=0;
stack[0].read(0,0,0);
for(LL i=1;i<=n;i++){
point op=stack[now];
while(top>0){
if(now<top&&slope(stack[now],stack[now+1])<A){
op=stack[++now];
continue;
}
break;
}
dp[i]=dp[op.idx]+EFF;
point next;
next.read(set_x(i),set_y(i),i);
while(top>0){
op=stack[top];
if(top>0&&slope(op,next)<slope(stack[top-1],op)){
top--;
if(now>top)now--;
continue;
}
break;
}
stack[++top]=next;
}
printf("%lld",dp[n]);
}
int main(){
/*freopen("input.txt","r",stdin); freopen("output.txt","w",stdout);*/
scanf("%lld%lld",&n,&l);
for(LL i=1;i<=n;i++){
scanf("%lld",&len[i]);
}
pre[1]=len[1];
for(LL i=2;i<=n;i++){
pre[i]=pre[i-1]+len[i];
}
DP();
return 0;
}
细节
1.最好在单调栈中最开始加一个虚拟点(原点),处理起来更方便,同时数组从1开始编号,有可能溢出时使用long long int类型
2.要看好维护的是上凸壳还是下凸壳,这两种的判断符号不同,而且最优解指针要注意不能越界
3.要输出最终解。。。