浅谈斜率优化dp
Ⅰ、前置知識(shí)
\(y=kx+b\)
\(k\)叫斜率,\(b\)叫截距
\((x_1,y_1)\)\((x_2,y_2)\)兩點(diǎn)連成的直線的斜率\(k=\frac{y1-y2}{x1-x2}\)
Ⅱ、拋出問題
洛谷板子
題目描述
\(n\)個(gè)任務(wù)排成一個(gè)序列在一臺(tái)機(jī)器上等待完成(順序不得改變),這\(n\)個(gè)任務(wù)被分成若干批,每批包含相鄰的若干任務(wù)。從時(shí)刻\(0\)開始,這些任務(wù)被分批加工,第\(i\)個(gè)任務(wù)單獨(dú)完成所需的時(shí)間是\(T_i\)。在每批任務(wù)開始前,機(jī)器需要啟動(dòng)時(shí)間\(S\),而完成這批任務(wù)所需的時(shí)間是各個(gè)任務(wù)需要時(shí)間的總和(同一批任務(wù)將在同一時(shí)刻完成)。每個(gè)任務(wù)的費(fèi)用是它的完成時(shí)刻乘以一個(gè)費(fèi)用系數(shù)\(F_i\)。請(qǐng)確定一個(gè)分組方案,使得總費(fèi)用最小。
例如:\(S=1\);\(T=\{1,3,4,2,1\}\);\(F=\{3,2,3,3,4\}\)。如果分組方案是\(\{1,2\}\)、\(\{3\}\)、\(\{4,5\}\),則完成時(shí)間分別為\(\{5,5,10,14,14\}\),費(fèi)用\(C=\{15,10,30,42,56\}\),總費(fèi)用就是\(153\)。
輸入輸出格式
輸入格式:
第一行是\(n(1\leq n\leq5000)\)。
第二行是\(S(0\leq S\leq50)\)。
下面\(n\)行每行有一對(duì)數(shù),分別為\(T_i\)和\(F_i\),均為不大于\(100\)的正整數(shù),表示第\(i\)個(gè)任務(wù)單獨(dú)完成所需的時(shí)間是\(T_i\)及其費(fèi)用系數(shù)\(F_i\)。
輸出格式:
一個(gè)數(shù),最小的總費(fèi)用。
輸入輸出樣例
輸入樣例#1:
5
1
1 3
3 2
4 3
2 3
1 4
輸出樣例#1:
153
Ⅲ、分析問題
首先這題\(O(n^2)\)可以艸過
但是\(O(n^2)\)過了這題講斜率優(yōu)化毫無意義QAQ
所以請(qǐng)自動(dòng)將數(shù)據(jù)范圍改成\((1\leq n\leq500000)\)
先來看一眼普通\(dp\)\(O(n^2)\)怎么寫
設(shè)
\(f[i]\)表示處理到第\(i\)個(gè)任務(wù),前\(i\)個(gè)的最小費(fèi)用
\(t_i\)表示時(shí)間的前綴和
\(c_i\)表示費(fèi)用的前綴和
考慮從\(j\)轉(zhuǎn)移到\(i\),表示\(j+1\)到\(i\)打包到一批
則狀態(tài)轉(zhuǎn)移方程為
\[f[i]=\min_{j=1}^{i}\{f[j]+s\times(c_n-c_j)+t_i\times(c_i-c_j)\}\]
由于之前哪些任務(wù)被分成一批不好處理,所以可以直接加上\(s\times(c_n-c_j)\)當(dāng)作對(duì)后續(xù)狀態(tài)的處理
然后推式子
將\(j\)看作一個(gè)變量,然后去掉\(\min\),得到
\[f[i]=f[j]+s\times(c_n-c_j)+t_i\times(c_i-c_j)\]
拆括號(hào)
\[f[i]=f[j]+s\times c_n-s\times c_j+t_i\times c_i-t_i\times c_j\]
移項(xiàng)
\[f[i]-s\times c_n+s\times c_j-t_i\times c_i+t_i\times c_j=f[j]\]
\[f[j]=f[i]-s\times c_n+s\times c_j-t_i\times c_i+t_i\times c_j\]
提取公因式
\[f[j]=c_j\times(s+t_i)+f[i]-s\times c_n+-t_i\times c_i\]
此時(shí)式子推成這樣
再看一眼前置知識(shí)
\(y=kx+b?\)
此時(shí)我們的式子就像一條直線解析式!
\[x=c_j,y=f[j],k=s+t_i,b=f[i]-s\times c_n+-t_i\times c_i\]
我們想要最小化\(f[i]\),就是最小化\(b\)
而我們此時(shí)要做的,就是用一條已知斜率的直線,利用已有的坐標(biāo),找到一個(gè)最小的\(b\)
如圖,現(xiàn)在處理到\(i\),則共有\(i-1\)個(gè)坐標(biāo)為\((z_j,f[j])(1\leq j<i)\)的點(diǎn)
如圖所示
右下角為當(dāng)前處理的斜率為\(s+t_i\)的直線,我們要將它向上平移,直到和上方\(i-1\)個(gè)點(diǎn)中的一個(gè)相交
顯然要找的點(diǎn)\(j\)(也叫決策點(diǎn))一定在圖形的凸包上
又很顯然,決策點(diǎn)一定在下凸殼上,因?yàn)橄峦箽ど系狞c(diǎn)顯然比上凸殼更優(yōu)
又很顯然,決策點(diǎn)一定在下凸殼的右半側(cè),因?yàn)?span id="ozvdkddzhkzd" class="math inline">\(k\)(即\(s+t_i\))一定大于\(0\)
叕很顯然找到?jīng)Q策點(diǎn)之后是這樣的
如何找到?jīng)Q策點(diǎn)?
觀察可以知道,凸包上的直線的斜率具有單調(diào)性
二分!
每次二分一個(gè)點(diǎn),\(check\)這個(gè)點(diǎn)左側(cè)的直線的斜率是否小于\(s+t_i\),右側(cè)的斜率是否大于\(s+t_i\),如果是就證明找到了決策點(diǎn)
手玩一下更好理解
找到?jīng)Q策點(diǎn)之后,很明顯,決策點(diǎn)左邊所有的直線的截距都要大于\(s+t_i\),所以左邊的所有點(diǎn)都沒有當(dāng)前點(diǎn)優(yōu)
于是拿單調(diào)隊(duì)列存一下凸包是上的點(diǎn),如果沒有當(dāng)前直線優(yōu)則踢掉
找到?jīng)Q策點(diǎn)后,相當(dāng)于找到了\(j\),更新\(f[i]\)
更新完\(f[i]\)后,為了方便后續(xù)的查找,將\((c_i,f[i])\)插入凸包,并且維護(hù)一下凸包的單調(diào)性
最后輸出\(f[n]\)即可
代碼:
轉(zhuǎn)載于:https://www.cnblogs.com/hzf29721/p/10423775.html
總結(jié)
- 上一篇: 学Python该看什么书?所有方向的精华
- 下一篇: 会声会影应用