眾所周知,斜率優化$dp$是很(mei)優(luan)秀(yong)的演算法。
所以,就和博主一起來學習吧。
例題一:任務安排
【題目描述】
有$n$個任務排成乙個序列在一台機器上等待執行,它們的順序不得改變。機器會把這$n$個任務分成若干批,每一批包含連續的若干個任務。從時刻$0$開始,任務被分批加工,執行第$i$個任務所需的時間是$t_$。另外,在每批任務開始前,機器需要$s$的啟動時間,故執行一批任務所需的時間是啟動時間$s$加上每個任務所需時間之和。
乙個任務執行後,將在機器中稍作等待,直至該批任務全部執行完畢。也就是說,同一批任務將在同一時刻完成。每個任務的費用是它的完成時刻乘以乙個費用係數$c_$。
請為機器規劃乙個分組方案,使得總費用最小。
【分析】通過題目描述,我們可以很輕鬆的看出這是乙個$dp$題目。
我們設$sumc[i]$表示費用的字首和,$sumt[i]$表示時間的字首和。
所以我們可以列出$dp$方程:
$$dp[i]=min(dp[i],sumt[i]*(sumc[i]-sumc[j])+s*(sumc[n]-sumc[j])+dp[j])$$
但是,這個$dp$轉移是$n^2$的,在$3\times10^5$次方的資料範圍下會t到天上。
於是,我們考慮將$dp$方程化簡:
$$dp[i]=dp[j]-(s+sumt[i])*sumc[j]+sumt[i]*sumc[i]+s*sumc[n]$$
看到這個式子,依然沒有什麼頭緒,所以我們可以找到兩個決策點k,j,假設k優於j,則
$$dp[j]-(s+sumt[i])*sumc[j]+sumt[i]*sumc[i]+s*sumc[n]\geq dp[k]-(s+sumt[i])*sumc[k]+sumt[i]*sumc[i]+s*sumc[n]$$
繼續整理式子
$$dp[j]-(s+sumt[i])*sumc[j]\geq dp[k]-(s+sumt[i])*sumc[k]$$
$$dp[j]-dp[k] \geq s*sumc[j]-sumt[i]*sumc[j]-s*sumc[k]+sumt[i]*sumc[k]$$
$$dp[j]-dp[k] \geq (s+sumt[i])*(sumc[j]-sumc[k])$$
$$\frac \geq (s+sumt[i])$$
可以發現,左邊的式子是形如$\frac-y_}-x_}$的式子,而右邊的式子是乙個常量。
我們可以想到斜率!構建乙個平面直角座標系,以$sumc[i]$為橫座標,以$dp[i]$為縱座標:
根據剛剛推出的$dp$,決策點k所在的直線的斜率只有大於它之前所有的點,小於它之後所有點的斜率,它才能被選中成為乙個最優解。因為若k點最優,則應滿足k點所在直線的截距是所有點中最小的。所以,這個k點應處於乙個「下凸殼」的頂點處。因為在所有可能成為最優決策的點集中,點的編號與直線斜率都是遞增的。根據這個性質,我們可以用單調佇列來維護可能成為最優決策的點集。當單調佇列頭的兩個點組成直線的斜率比當前直線的斜率$\leq s+sumt[i]$小的時候,就將其彈出。當單調佇列尾的兩個點與當前點組成的不是「下凸殼」時,就將其彈出。
總結一下:當$dp$方程有a[i]*b[j]的項時:
①設決策點k優於j,化簡方程,看是否能化簡成不等式左邊為形如$\frac-y_}-x_}$,右邊為乙個常數的形式,若不能,則採用暴力;
②看這道題是維護上凸殼還是下凸殼。
下面給出任務安排的**:
#include#includeview code#include
#include
#include
using
namespace
std;
int sumt[300001],sumc[300001
];int dp[300001],q[300001
];int x(int i)
int y(int i)
int slope(int i,int j)
intmain()
int l=1,r=1
; memset(dp,
0x3f,sizeof
(dp));
dp[0]=0
;
for(i=1;i<=n;i++)
cout
<
}
既然學會了方法,那麼接下來的事情就是刷題了。
【例題1】任務安排公升級版 link
這題和上題一模一樣,但是$sumc[i]$有可能是負的。所以下凸殼的性質就不能用了,我們就不能使用上一問的單調佇列,所以需要二分遍歷整個點集。
【例題2】cats transport link
這題思維難度較大,但是$dp$方程很簡短。我們將飼養員正好接到貓的時間預處理,然後將其從小到大排序。我們設$dp[i][j]$表示前$i$個飼養員接前$j$只貓的時間。因為乙個人接到第j只貓時,可以接到前面所有貓。所以我們列舉斷點,可以列出$dp$方程:$dp[i][j]=min(dp[i][j],dp[i-1][x]+(j-x)*a[j]-sum[j])$,其中$a_$表示正好接到第$i$只貓的出發時間,$sum[i]$表示$a[i]$的字首和。
【例題3】玩具裝箱 link
這題思維難度很小,但是式子的化簡十分複雜。我們把$j-i$設為a,$sum[k]-sum[i]$設為$suma$,$sum[j]-sum[i]$設為$sumb$,採用換元的思想降低化簡難度。最終化簡出$x=sum[i]+i+l+1$,$y=dp[i]+(sum[i]+i+l+1)^2$。
【例題四】倉庫選址 link
很普通的一道斜率優化,dp方程留給大家自己推吧。
大家還可以試試2023年的江蘇省選火星藏寶圖,很好的思維題目。
斜率優化 DP
我們知道,有些dp方程可以轉化成dp i f j x i 的形式,其中f j 中儲存了只與j相關的量。這樣的dp方程我們可以用單調佇列進行優化,從而使得o n 2 的複雜度降到o n 可是並不是所有的方程都可以轉化成上面的形式,舉個例子 dp i dp j x i x j x i x j 如果把右邊...
dp斜率優化
我們知道,有些dp方程可以轉化成dp i f j x i 的形式,其中f j 中儲存了只與j相關的量。這樣的dp方程我們可以用單調佇列進行優化,從而使得o n 2 的複雜度降到o n 可是並不是所有的方程都可以轉化成上面的形式,舉個例子 dp i dp j x i x j x i x j 如果把右邊...
斜率優化DP
斜率優化主要解決的是轉移方程中存在乙個同時與i和j有關的部分時的優化問題 dp i min dp j a i b j 0 include using namespace std typedef long long ll const int maxn 1e5 5 ll a maxn b maxn dp...