初探斜率优化
dp是一個在noip必考的項目(據說今年又要出新算法了),于是在機房老師的引導下,學習了斜率優化dp(真是一個好東西)
下面說一下我的心得吧:
? 首先對于斜率優化呢
? 這是一種無法用直接單調隊列優化的算法,由于它的轉移方程中存在著一個或多個會根據當前狀態有關的量,故單調隊列無法直接優化,而這里就用到了斜率優化。
? 下面給出一道例題
P3195 [HNOI2008]玩具裝箱TOY
題目描述
P教授要去看奧運,但是他舍不下他的玩具,于是他決定把所有的玩具運到北京。他使用自己的壓縮器進行壓縮,其可以將任意物品變成一堆,再放到一種特殊的一維容器中。P教授有編號為?1\cdots N1?N?的?NN?件玩具,第?ii?件玩具經過壓縮后變成一維長度為?C_iCi??.為了方便整理,P教授要求在一個一維容器中的玩具編號是連續的。同時如果一個一維容器中有多個玩具,那么兩件玩具之間要加入一個單位長度的填充物,形式地說如果將第?ii?件玩具到第?jj?個玩具放到一個容器中,那么容器的長度將為?x=j-i+\sum\limits_{k=i}^{j}C_kx=j?i+k=i∑j?Ck??制作容器的費用與容器的長度有關,根據教授研究,如果容器長度為?xx?,其制作費用為?(X-L)^2(X?L)2?.其中?LL?是一個常量。P教授不關心容器的數目,他可以制作出任意長度的容器,甚至超過?LL?。但他希望費用最小.
感謝@ACの666 提供的Latex題面
輸入輸出格式
輸入格式:
?
第一行輸入兩個整數N,L.接下來N行輸入Ci.1<=N<=50000,1<=L,Ci<=10^7
?
輸出格式:
?
輸出最小費用
?
輸入輸出樣例
輸入樣例#1:?復制
5 4 3 4 2 1 4輸出樣例#1:?復制
1我們發現這題的dp方程十分的簡單
? f[i]=min{f[j]+(sum[i]-sum[j]+i-j-l-1)^2}(看不懂或推不出的請學好dp再來)
方便起見 我們定義xa[i]=sum[i]+i? xb[i]:=sum[i]+i+l+1
轉移方程變為了f[i]=min{f[j]+(xa[i]-xb[j])^2}(0<=j<i)
?數據范圍一看就不是O(n^2)能承受的,我們必然要對其中的一維狀態或是一維轉移優化
若是優化狀態。。。。(O(1)的狀態那不就變貪心了嗎???,某些題的貪心性質就是這樣被發現的)
但是這題我們沒有發現可以貪心的線索,那我們只能優化轉移了
這里我們發現對于一個新的i的轉移有一個sum[i]和它-l的平方影響?,很明顯無法簡單使用單調隊列優化轉移
在這里,我們不妨假設有j<k,對于i的轉移k比j優
則就會得到
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?f[j]+(xa[i]-xb[j])^2>f[k]+(xa[i]-xb[k])^2
打開這個式子:? ? ? ? ?f[j]+xa[i]^2+xb[j]^2-2*xa[i]*xb[j]>f[k]+xa[i]^2+xb[k]^2-2*xa[i]*xb[k]
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? f[j]+xb[j]^2-(f[k]+xb[k]^2)<2*xa[[i]*(xb[j]-xb[k])
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?這里就會很容易發生一些錯誤
由于j<k,那么這題的sum數組又是單調遞增的,則xb[j]-xb[k]其實是一個負數,當我們把他除過去的時候,不等號就會改變方向
我們發現? ? ??(f[j]+xb[j]^2-(f[k]+xb[k]^2))/(2*(xb[j]-xb[k]))>xa[i]
?左邊只剩下了關于j和k的量,不妨用g(j,k)表示(f[j]+xb[j]^2-(f[k]+xb[k]^2))/(2*(xb[j]-xb[k]))
于是我們發現當g(j,k)<xa[i]時k就會更優
反之g(j,k)>xa[i]時j就會更優,神奇的事情發生了
我們發現如果存在j1<j2<j3,g(j1,j2)>g(j2,j3)時無論xa[i]如何取值,在這里j2這個轉移一定不會是最優的
那我們只要維護一個G單調不降就可以了
同時發現若xa[i]大于了單調隊列中最小的一個,后面的元素都會沒有用,所以在隊首維護一個單調性把小于xa[i]的值都彈出,每次取隊首的元素作為最優的轉移對象,那么時間復雜度就會降到O(n)的時間范圍,一個元素只會進隊一次出隊一次,均攤O(1)
那么為什么要叫斜率優化呢,我們發現g(j,k)里可以抽象的把j和k看做兩個點而g(j,k)可以看做是j到k的斜率,維護g的單調性就相當與維護一個二維的凸包,每次出現xa[i],就是相當與用一條斜率為xa[i]的線去在這個凸包上截取最近的一個點。
那么斜率優化也就講到這里了
安利一波代碼
var
n,l,i,j,head,tail:longint;
a,sum,xb,xa,f,q:array[0..1000000] of int64;
function g(a,b:longint):double;
begin
? exit((f[a]-f[b]+xb[a]*xb[a]-xb[b]*xb[b])/((xb[a]-xb[b])<<1));
end;
begin
? readln(n,l);
? for i:=1 to n do
? begin
? ? read(a[i]);
? ? sum[i]:=sum[i-1]+a[i];
? ? xa[i]:=sum[i]+i;
? ? xb[i]:=sum[i]+i+l+1;
? end;
? xb[0]:=l+1;
? head:=1; tail:=1;
? q[1]:=0;
? for i:=1 to n do
? begin
? ? while (head<tail) and (g(q[head+1],q[head])<xa[i]) do inc(head);
? ? j:=q[head];
? ? f[i]:=f[j]+(xa[i]-xb[j])*(xa[i]-xb[j]);
? ? while (head<tail) and (g(q[tail],q[tail-1])>g(q[tail-1],i)) do dec(tail);
? ? inc(tail); q[tail]:=i;
? end;
? writeln(f[n]);
end.
轉載于:https://www.cnblogs.com/by-w/p/9608092.html
總結
- 上一篇: 计算机无法投影,如果无法连接计算机和投影
- 下一篇: audio 应用-Python 分析工具