您的位置:首页 > 其它

学习笔记--斜率优化

2017-11-04 19:29 239 查看
上篇文章讲了凸包,那么这篇文章我们来讲讲跟凸包联系比较大的斜率优化:

所谓斜率优化指的是对于一系列我们不能够用各种鬼畜的数据结构来维护,达到降低时间复杂度的目的的鬼畜题。。。但是DP方程有一定特殊性,满足决策单调之类的。。。我们就可以采用这种数形结合的方法来乱搞了。

那么来看一道例题(JZOJ 1132):

F国际航空公司在世界范围有n个国际机场。第i 个国际机场到中心机场的距离为di,i=1,…,n。从国际机场j到国际机场i的飞行费用为w(i,j)=s+(dj-di)2,s为地面加油费用。从任何国际机场飞往中心机场的飞机可以在任一国际机场加油后继续飞行。飞机加油问题要求确定从距中心机场最远的国际机场飞到中心机场的最少费用。

对于给定的n个国际机场到中心机场的距离d1,d2,……,dn,以及地面加油费用s,编程计算从距中心机场最远的国际机场飞到中心机场的最少费用。

输入:

第一行有2个整数n和s,表示有n(n <= 400000)个国际机场(不包括中心机场),地面加油费用s。接下来的1 行中每行有n个整数d1,d2, ……,dn,表示给定的n个国际机场到中心机场的距离。

样例输入: 样例输出:

5 10 64

1 3 6 7 10

这一题乍一看非常简单,嗯,对di排序后随手敲出DP方程,设状态f[i]表示到第i个国际机场的最小费用,然后瞎列个转移方程:

f[i]=min(f[i],f[j]+(d[j]−d[i])2+s)

嗯嗯,欣喜的以为问题解决了,然后瞄到数据范围n<=400000。。。flag不要太早立啊,于是我们转头想优化,取最小值容易想到单调队列取最小,然而 (d[j]
1558a
−d[i])2 不是常数就不能怎么做,这时候就需要用到斜率优化了。。

我们对方程做如下变形:

f[i]=f[j]+d[j]2+d[i]2+2d[i]d[j]+s

则有−2d[i]d[j]+f[i]=f[j]+d[i]2+d[j]2+s

这个式子就很像我们数学中二维平面的斜截式啦 kx+b=y

kx=−2d[i]d[j],y=f[j]+d[i]2+d[j]2+s,b=f[i]

那么我们就可以想我们要使f[i]最小,那么它在y轴的截距就要尽量小,我们对于每一个

点f[i]从f[j] 里选择一个作为决策点,那么对于直线y=kx+b 我们将它向上平移,

碰到的第一个点就是最优点了(此时b最小)。。我们可以求出每个可能决策点之间的连线的斜率,找到斜率和它最接近的。

那么我们就需要对于两个平面点k<j化成斜率式,大约就是这样:

(f[k]+d[k]2−(f[j]+d[j]2))/(d[k]−d[j])

嗯,然后很显然的由于我们要找的斜率2d[i] 单调上升,所以维护一个下凸壳啊!!!

那些肯定不能成为决策点的点要删去嘛,由于下凸壳,斜率单升,所以开个单调队列维护二元组(i,j) 就ok了,于是乎复杂度O(n)了.

代码:

# include<cstdio>
# include<algorithm>
using namespace std;
const int N = 4e5 + 10;
int q
,d
,f
,g
;
int i,n,h,t,s;
double xie(int k,int j)
{
return (g[k] - g[j]) * 1.0 / (d[k] - d[j]);
}
int main()
{
scanf("%d%d",&n,&s);
n++;
for (i = 2;i <= n; i++)
scanf("%d",d + i);
sort(d + 1,d + n + 1);
g
= d
* d
;
h = 1,t = 1; q[h] = n;
for (i = n - 1;i >= 1; i--)
{
if (d[i] == d[i + 1]) { f[i] = f[i + 1]; continue; }
while (h < t && xie(q[h],q[h + 1]) >= 2.0 * d[i]) h++;
f[i] = f[q[h]] + (d[q[h]] - d[i]) * (d[q[h]] - d[i]) + s;
g[i] = f[i] + d[i] * d[i];
while (h < t && xie(q[t - 1],q[t]) < xie(q[t],i)) t--;
q[++t] = i;
}
printf("%d",f[1]);
return 0;
}


偷懒把f[i]+d[i]2 直接设成 g[i]…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp