这是一道经典的斜率优化
它就是任务安排……
首先可以看出他是一道 DP
,然后就有了 O ( n 3 ) \text{O}(n^{3}) O(n3) 的优秀时间复杂度,在此就默认各位已经是基础 DP
高手,因此不做过多的讲解。
可是要切掉这道根本不需要斜率优化的水题,至少要考虑 O ( n 2 ) \text{O}(n^{2}) O(n2) 的算法。
这里需要用到费用提前处理思想。
众所皆知:我们每分一组,后面未进行分组的一定会增加费用。
所以只需要提前将这些费用加上即可。
这样我们的 d p dp dp 数组也降了一维,定义 d p i dp_i dpi 表示完成前 i i i 个任务的最小费用,进而枚举上一次分组的最后任务 j j j( 0 ≤ j < i 0 \le j < i 0≤j<i),即是 j + 1 ∼ i j+1 \sim i j+1∼i 属于同一分组,这一组等待时长 T T T 会对之后的分组都增加 T T T 的时间。
还需要预处理出一个 s u m t sumt sumt 数组,表示时间前缀和,一个 s u m c sumc sumc 数组,表示费用前缀和。
所以有了如下的状态转移方程:
f i = min 0 ≤ j < i f j + ( s u m c i − s u m c j ) ∗ s u m t i + ( s u m c n − s u m c j ) ∗ s f_i=\min\limits_{0 \le j fi=0≤j<iminfj+(sumci−sumcj)∗sumti+(sumcn−sumcj)∗s
很明显,它的时间复杂度为 O ( n 2 ) \text{O}(n^{2}) O(n2),过洛谷那题轻轻松松,于是就有了一些恶臭版本,例如某人将 n n n 的范围开到了 1 0 5 10^{5} 105,本来 1 s 1s 1s 过了没什么障碍,可是他却将时限下调至 50 m s 50ms 50ms,怎么办呢?
@%#%&&&%%¥@¥*@……
关于斜率优化,初次接触记忆犹新SDOI2016征途,很明显的一道 DP
,于是年轻的我就开“征”了,结果后来看到标签大大四个字——斜率优化
,当场蒙B,开始摆烂……
其实它的斜率优化真的很简单(如果你愿意花几十分钟去想一想……)。
在此引用某位大佬的题解,然后开始介绍那道被疯狂卡常的题目。
首先,什么是斜率。
我们令它为 k k k,我们熟知一次函数表达式 y = k x + b y=kx+b y=kx+b,其中 k k k 就是斜率。
而对于斜率 k k k 的计算则更加简单。
我们假设有两点 P 1 ( x 1 , y 1 ) P_1(x1,y1) P1(x1,y1) 和 P 2 ( x 2 , y 2 ) P_2(x2,y2) P2(x2,y2) 在该一次函数上,那么根据解析式可以列出方程:
x1*k+b=y1------------1式
x2*k+b=y2------------2式
将 1 1 1 式减去 2 2 2 式,则可以得到 ( x 1 − x 2 ) ∗ k = y 1 − y 2 (x1-x2)*k=y1-y2 (x1−x2)∗k=y1−y2,整理一下得到: k = y 1 − y 2 x 1 − x 2 k=\frac{y1-y2}{x1-x2} k=x1−x2y1−y2.
有了这些,我们可以进行之后的学习。
我们在之前的算法中运用到了以下状态转移方程:
f i = min 0 ≤ j < i f j + ( s u m c i − s u m c j ) ∗ s u m t i + ( s u m c n − s u m c j ) ∗ s f_i=\min\limits_{0 \le j fi=0≤j<iminfj+(sumci−sumcj)∗sumti+(sumcn−sumcj)∗s.
我们去掉 min \min min 并且拆开式子结合后可以得到:
f j = ( s u m t i + s ) ∗ s u m c j + ( f j − s u m t i ∗ s u m c i − s ∗ s u m c n ) ( o ≤ j < i ) f_j=(sumt_i+s)*sumc_j+(f_j-sumt_i*sumc_i-s*sumc_n)(o \le j fj=(sumti+s)∗sumcj+(fj−sumti∗sumci−s∗sumcn)(o≤j<i)
我们对上面式子进行分析,发现变量只有 s u m c j sumc_j sumcj 和 f j f_j fj 两者,剩下的全是常量,于是我们不禁联想上文提到的一次函数,其中 s u m t i + s sumt_i+s sumti+s 为斜率, f j − s u m t i ∗ s u m c i − s ∗ s u m c n f_j-sumt_i*sumc_i-s*sumc_n fj−sumti∗sumci−s∗sumcn为截距(即为 b b b),这个转化非常重要!
直线的斜率永远是一个固定的值,截距未知,所以当截距最小时, f j f_j fj 也取得最小值
首先按照横坐标递增维护一个单调队列,再把每个点画上去,可以得到以下两种情况。