斜率优化详解(一)

本文详细介绍了斜率优化的概念,通过一个包装运输公司的优化问题阐述了斜率优化的必要性。作者分别讲解了公式法和图像法两种推导思路,强调了在解决实际问题中如何应用斜率优化降低时间复杂度。文中还通过图示解释了单调队列在维护决策点时如何形成下凸壳,帮助读者深入理解这两种方法。
摘要由CSDN通过智能技术生成

花了好长的时间,我终于学会了斜率优化。
说实在的,斜率优化其实并不难,但是老师说的和网上的博客写的不够详细,导致我在很长一段时间都无法弄懂。
言归正传,我们从一道题讲起

【题意】
一个包装运输公司,只生产一种容量为L的包装盒。如果要装容量为X的物品,则需要花钱修改包装盒的尺寸,花费为(X - L)^2。
现在有N个物品需要装入包装盒,每个物品的容量为Ci。
(1)、可以多个物品装入一个包装盒
(2)、同一个包装盒的物品编号必须是连续的
(3)、同个包装盒的物品排成直线,相邻两个物品之间要加一块容量为1的隔板。
目标:总花费最小。

【输入格式】
第一行两个整数,分别为N和L。
下来N个数字Ci,按编号从小到大输入每个物品的容量。
1<=N<=50000,1<=L,Ci<=10^7
【输出格式】
一个整数,总花费的最小值。
【样例输入】
5 4
3 4 2 1 4
【样例输出】
1

(这道题相信大家都做过了)。
先是推出dp公式
让 f[i] 表示1~i的最小花费。
只要思考几分钟,就能推出dp方程

f[i]=min(f[j]+(sum[i]-sum[j]+i-(j+1)-L)^2) (j<i)
 f[i]=min(f[j]+(sum[i]+i-sum[j]-j-1-L)^2) (j<i)
令s[i]=sum[i]+i,L=1+L
则f[i]=min(f[j]+(s[i]-s[j]-L)^2)

不过,这个dp的时间复杂度是O(N^2),对于1<=N<=50000的数据是一定会炸掉的

所以我们考虑用单调队列维护

于是把方程拆开得到

f[i] = min ( f[j] + s[i]^2 - 2*s[i]*(s[j]+L) + ( s[j]+L )^2 )

当我信心十足的开始打单调队列时,突然发现有一个 - 2 * s[i] * (s[j]+L)

也就是说,单调队列是用不了的,从而引入斜率优化

在我看了无数博客以后,我归纳总结出了斜率优化的两种不同推法

一种是利用化简公式证明决策单调性,然后推公式,我管它叫公式法

另外一种是将决策点转为(x,y),x和y都是代数式,并且带入平面直角坐标系,利用图像来进行斜率优化,我管它叫图像法

虽然两种方法的代码都是大同小异的,但是理解起来会有一些不同

我个人比较喜欢图像法(简单易懂)

一、公式法

注:因为用*号会出现一些奇怪的问题,所以我放到代码里面 & 这一段装载于caioj

1.证明决策单调性
 假设j1<j2<i,在状态i处的j2决策不比j1决策差(心里想着淘汰j1),
 即要满足:f[j2]+(s[i]-s[j2]-L)^2<f[j1]+(s[i]-s[j1]-L)^2
则对于i后的所有状态t,是否j2也不比就j1差?(术语:证明决策单调性)
 即f[j2]+(s[t]-s[j2]-L)^2 < f[j1]+(s[t]-s[j1]-L)^2
容易理解s[t]=s[i]+v
所以得到(1)不等式:f[j2]+(s[i]-s[j2]-L+v)^2<f[j1]+(s[i]-s[j1]-L+v)^2
因为已知(2)不等式:f[j2]+(s[i]-s[j2]-L  )^2<f[j1]+(s[i]-s[j1]-L  )^2
所以化简(1)不等式:把s[i]-s[j2]-L看成一个整体,v看成一个整体,得到
f[j2]+(s[i]-s[j2]-L  )^2 +2*v*(s[i]-s[j2]-L)+v^2 <f[j1]+(s[i]-s[j1]-L  )^2+2*v*(s[i]-s[j1]-L)+v^2
比较(2)不等式:
 左边多了一部分:2*v*(s[i]-s[j2]-L)+v^2
右边多了一部分:2*v*(s[i]-s[j1]-L)+v^2
所以我们只需要证:
2*v*(s[i]-s[j2]-L)+v^2<=2*v*(s[i]-s[j1]-L)+v^2
即:(s[i]-s[j2]-L)    <=    (s[i]-s[j1]-L)
即:     -s[j2]       <=         -s[j1]
即:s[j1]<s[j2]这是肯定的,所以得证。
 总结:对于当前i:j2比j1好,那么对于t(i<t)来说一样:j2一样比j1好,
 所以当前i选择j2,淘汰j1,以后的t也不会在j2存在的时候选择j1,
 所以i的时候就可以永久淘汰j1.

 2.求斜率方程:
 因为f[j2]+(s[i]-s[j2]-L)^2 < f[j1]+(s[i]-s[j1]-L)^2
展开:
f[j2]+(s[i]-L)^2-2*(s[i]-L)*s[j2]+s[j2]^2<f[j1]+(s[i]-L)^2-2*(s[i]-L)*s[j1]+s[j1]^2
即f[j2]-2*(s[i]-L)*s[j2]+s[j2]^2<f[j1]-2*(s[i]-L)*s[j1]+s[j1]^2
即f[j2]+s[j2]^2-2*(s[i]-L)*s[j2]<=f[j1]+s[j1]^2-2*(s[i]-L)*s[j1]
即[ (f[j2]+s[j2]^2)-(f[j1]+s[j1]^2) ] <  2*(s[i]-L)*s[j2]-2*(s[i]-L)*s[j1]
即[ (f[j2]+s[j2]^2)-(f[j1]+s[j1]^2) ] /(s[j2]-s[j1]) <  2*(s[i]-L)
对于j来说:
 制造的点坐标
Y=f[j]+s[j]^2
 X=s[j]

我们用队列list在存有意义的决策点,list中相邻两点的斜率递增(队列中的点形成一个下凸壳),而且都
 大于2*(s[i]-L),那么队列头对于i来说就是最优决策点。
 加入决策i时,令队尾为list[tail],前一个为list[tail-1]
斜率函数slop(点1,点2)
 满足:  slop(list[tail-1],list[tail]) > slop(list[tail],i)  时,
 那么队尾list[tail]在三者(list[tail-1],list[tail],i)对于未来的t绝对不会是最优的策略,所以将
 其弹出tail--;
最后遇到了:slop(list[tail-1],list[tail]) <  slop(list[tail],i),保证了队列的相邻两点的斜率递
 增所以加入i: list[++tail]=i;

呵呵,这么一大堆文字真是不好懂,慢慢品味吧^_^

如果是数论很好的同学,那么这种方法是很适合你的

二、图像法

在O(N^2 ) 的暴力代码中,求f[i]的值要for(1-i)&#

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值