以高中OJ1749(也就是B组8.7T2)为例。
这题50分的做法是设f[i]表示第i个点的答案,那么从n推到1,则f[i]=min(f[j]+(j-i)*a[i]+b[j])(j>i),最后的答案就是f[1],但这样会超时。
若存在j,k(j<k),对于一个i,j比k优,那么就是f[j]+(j-i)*a[i]+b[j]<f[k]+(k-i)*a[i]+b[k],我们来化简一下这条式子:
f[j]+j*a[i]-i*a[i]+b[j]<f[k]+k*a[i]-i*a[i]+b[k]
f[j]+j*a[i]+b[j]<f[k]+k*a[i]+b[k]
令g[x]=f[x]+b[x]
g[j]+j*a[i]<g[k]+k*a[i]
a[i]*(j-k)<g[k]-g[j]
因为j<k所以j-k<0
-a[i]<(g[k]-g[j])/(k-j)
而我们发现,(g[k]-g[j])/(k-j)就相当于一个斜率(点(x1,y1),(x2,y2)之间斜率就相当于(y1-y2)/(x1-x2)),所以我们可以针对所有大于i个点做一张函数图,那么显然在函数图上找一个类似于凸多边形的一部分的图像一定是最优的,而一段形如凸多边形的斜率的值是递增的。现在来说说怎样进行查询和插入操作:
1、插入:假设当前要把i点插入到图中,那么我们从后往前枚举当前图中剩余的点,设与i点最近的点为j,次近的点为k,若(g[k]-g[j])/(k-j)>(g[j]-g[i])/(j-i),那么就把j踢掉。一直重复这么做,知道直到找到一个不需要被踢的j为止。
2、查询:因为凸多边形的图像具有递增的性质,那么我们可以二分一个做最靠近i的x,使得-a[i]<(g[x+1]-g[x])/(x+1-x)(所有x要在凸多边形的图像中),然后直接用x来更新i。
注意:
1、在二分时,l的初始值为2。
2、若在图像中找不到一个合法的点,那么就取图像中最后一个点。
总结:
1、斜率优化适用于i与j有关系的题
2、在化简时,要按照这些步骤来:
(1)把括号之类的东西去掉,然后把重复部分去掉
(2)把只与j,k有关的部分用g表示出来
(3)把g放一边,把有i的放一边,并且把有i的那边合并同类项
(4)把i那边的j,k移项,表示出最终的斜率关系
在一个形如凸多边形的斜率集合中,若我们能找到一个点x,使得x前的斜率都小于-a[i]且x后的斜率都大于-a[i],这样就说明了x既比它前面的优,又比它后面的优,所以x是最优的。因为形如凸多边形的斜率是递增的,所以我们可以用二分找一个最接近-a[i]又比-a[i]大的。加点时则要保证斜率的递增性。
此外,有两点要注意:
1、在单调队列里的点必须是全部对当前要求的点是合法的。
2、判断斜率大小时要注意用乘法,防止除数为0,而且要写小于等于或大于等于。