总结
方程形如y=k*x+b 其中**需选择的决策点f[j]**作为x与y,**当前需求的f[i]**作为b,若需f[i]小,则维护下凸壳(斜率单增,想象直线上移与第一个点相交,截距则为f[i]),否则维护上凸壳。
单调队列维护凸壳斜率。
例题
任务安排
- 题目大意:给定n个任务,任务有完成时间和代价,任务总代价等于每个任务(完成时间*代价)的和,机器有启动时间s,任务需按顺序完成。现可以捆绑一些任务,机器只需启动一次,但这些任务完成时间等于最后一个任务的完成时间,问最小代价。
- 思路:sumc为代价前缀和,sumt为时间前缀和。设
f[i]表示完成前i个任务的代价,但无法确定机器启动次数,不好转移。此时可以费用提前计算,每开一次机器,就会让之后所有任务代价加一次。那就可以得到方程:
f[i]=min(f[j]+(sumc[i]−sumc[j])∗sumt[i]+s∗(sumc[n]−sumc[j]))(1<=j<i)此时复杂度是n方的。
- 优化:首先分离min:
f[i]=min(f[j]+sumc[i]∗sumt[i]−sumc[j]∗sum[i]+s∗sumc[n]−s∗sumc[j])
f[i]=min(f[j]−sumc[j]∗sumt[i]−s∗sumc[j])+sumc[i]∗sumt[i]+s∗sumc[n]
因为我们需要选择决策点j所以将j看做未知数
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的
- 代码实现见链接。