【dp】斜率优化

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_42725189/article/details/102758685

总结

方程形如y=k*x+b 其中**需选择的决策点f[j]**作为x与y,**当前需求的f[i]**作为b,若需f[i]小,则维护下凸壳(斜率单增,想象直线上移与第一个点相交,截距则为f[i]),否则维护上凸壳。
单调队列维护凸壳斜率。

例题

任务安排

  • 题目大意:给定n个任务,任务有完成时间和代价,任务总代价等于每个任务(完成时间*代价)的和,机器有启动时间s,任务需按顺序完成。现可以捆绑一些任务,机器只需启动一次,但这些任务完成时间等于最后一个任务的完成时间,问最小代价。
  • 思路:sumc为代价前缀和,sumt为时间前缀和。设 f [ i ] f[i] 表示完成前i个任务的代价,但无法确定机器启动次数,不好转移。此时可以费用提前计算,每开一次机器,就会让之后所有任务代价加一次。那就可以得到方程: f [ i ] = m i n f [ j ] + ( s u m c [ i ] s u m c [ j ] ) s u m t [ i ] + s ( s u m c [ n ] s u m c [ j ] ) ) 1 < = j < i f[i]=min(f[j]+(sumc[i]-sumc[j])*sumt[i]+s*(sumc[n]-sumc[j]))(1<=j<i) 此时复杂度是n方的。
  • 优化:首先分离min: f [ i ] = m i n ( f [ j ] + s u m c [ i ] s u m t [ i ] s u m c [ j ] s u m [ i ] + s s u m c [ n ] s s u m c [ j ] ) f[i]=min(f[j]+sumc[i]*sumt[i]-sumc[j]*sum[i]+s*sumc[n]-s*sumc[j])

f [ i ] = m i n ( f [ j ] s u m c [ j ] s u m t [ i ] s s u m c [ j ] ) + s u m c [ i ] s u m t [ i ] + s s u m c [ n ] f[i]=min(f[j]-sumc[j]*sumt[i]-s*sumc[j])+sumc[i]*sumt[i]+s*sumc[n]

因为我们需要选择决策点j所以将j看做未知数
f [ j ] = ( s u m t [ i ] + s ) s u m c [ j ] + f [ i ] s u m c [ i ] s u m t [ i ] + s s u m c [ n ] f[j]=(sumt[i]+s)*sumc[j]+f[i]-sumc[i]*sumt[i]+s*sumc[n]

发现这像一个一次函数y=kx+b,f[i]处于b,则需让b尽量小,即可单调队列维护

  • 一些需要注意的地方:所维护的x必须单增,否则需要平衡树动态插点查询,其次,k是单增的,则可以及时弹出队首。此时复杂度是n的
  • 代码实现见链接。

猜你喜欢

转载自blog.csdn.net/qq_42725189/article/details/102758685