斜率规划:dp式子转移时候取min/max时会从多个以有状态转移而来,斜率优化可以从一次函数的角度来排除无用的情况,从而对dp式子进行优化
每次枚举分组点j,对于每个分组点,后面的所有都会加上一个空余时间
用tsum和wsum分别表示t与w的前缀和
则有:
式子时间复杂度为O(n^2)
dp[0]=0;
for(int i=1;i<=a;i++){
for(int j=0;j<i;j++){
dp[i]=min(dp[i],dp[j]+tsum[i]*(wsum[i]-wsum[j])+(wsum[a]-wsum[j])*b);
}
}
来回看dp式子,去掉min,则有
因为j是变量,所有把式子看成一次函数的式子
这里令y=dp[j], k=(b+tsum[i]), x=wsum[j], b=dp[i]-wsum[i]*tsum[i]-b*wsum[a]
发现b中除了dp[i]以外都是定值,则容易发现只需求出来b的最小值
那么用单调队列保存凸包
push_back(0);
for(int i=1;i<=a;i++){
while(!empty()&&(dp[q[l+1]]-dp[q[l]])<=(b+tsum[i])*(wsum[q[l+1]]-wsum[q[l]])) pop_front();//相邻两个点的k值如果小于需要的k,那么这个点一定不是最优的
dp[i]=dp[q[l]]-(b+tsum[i])*wsum[q[l]]+b*wsum[a]+tsum[i]*wsum[i];
while(!empty()&&(dp[i]-dp[q[r]])*(wsum[q[r]]-wsum[q[r-1]])<=(dp[q[r]]-dp[q[r-1]])*(wsum[i]-wsum[q[r]])) pop_back();//维护凸包
push_back(i);
}
因为时间有正有负,所以dp数组没有单调性
但是仍有一个性质:一个点若为所求的点,那么这个点左边的线段斜率k1,右边线段斜率k2,有
k1<k<k2
那么可以用二分求解
int binary_search(ll x){
int l1=l,r1=r,ans=0;
while(l1<=r1){
int mid=(l1+r1)/2;
if(dp[q[mid+1]]-dp[q[mid]]>=x*(wsum[q[mid+1]]-wsum[q[mid]])){
ans=r1;
r1=mid-1;
}else{
l1=mid+1;
}
}
return l1;
}