dp亂寫3:論dp在不在dp中(但在dp範疇)內的應用
最近正兒八經的學習了dp,有一些題目非常明顯看出來就是dp了
比如說:過河卒、方格取數、導彈攔截、加分二叉樹、炮兵陣地
更加明顯的還有:采藥、裝箱問題、過河、金明的預算方案。
今天來談談dp的dp在不在dp中(但在dp範疇)內的應用(簡稱dp的應用)
dp其實可以用貪心來優化,有些基本不可能的情況就可以直接省略了。
dp其實可以用數據結構來優化,取最大值最小值用堆。。。
dp其實不一定是dp,也可以是一種思想,的簡稱dp思想,
就是用前面的一個或者兩個狀態來推出現在狀態的可能,解決一些問題有神效。
dp的空間基本上是n^2或者m^2(用滾存也是少數啊3轉2其實也可以滾的啊)
如果看到n=1,000;m=1,000的數據範圍就想到dp啦。
但是用dp思想一定沒錯啦。。。
下面有個小例子來闡述一下p在不在dp中(但在dp範疇)內的應用(簡稱dp的應用)
【題目名稱】遊覽(sightseeing.pas/c/cpp)
【題目描述】周末到了,gnocuil打算去逛遊樂園。他所去的遊樂園可以看做一個N*M的網格,每個網格上都是一個景點。遊樂園的入口在(1,1),出口在(N,M)處。遊樂園有一個不成文的規定:遊覽時,只能朝著坐標增大的方向走,因為如果坐標減小,會很不吉利。換句話說,每次只能向右方、正下方走。當然,只可以走到相鄰的景點,不可以沿著斜線走。
對於走到的每個景點,gnocuil都會得到一個快樂度A[i,j]。同時,遊覽會使他疲倦,每個景點都會使他增加B[i,j]的疲勞度。
【輸入文件】第一行是兩個整數N,M,表示遊樂園的大小。
接下來N行每行M個正整數,表示這個景點的快樂度。
接下來N行每行M個正整數,表示這個經典的疲勞度。
【輸出文件】只有一個實數,表示最大價值。精確到小數點後5位小數。
【輸入樣例】
2 2
1 2
1 1
1 1
2 1
【輸出樣例】
1.33333
【樣例說明】只有兩條線路:(1,1)->(1,2)->(2,2)的價值是(1+2+1)/(1+1+1)=1.33333,(1,1)->(2,1)->(2,2)的價值是(1+1+1)/(1+2+1)=0.75000。
現在我要闡述的是這題怎麽用dp(思想)來解題。
首先,直接DP顯然錯誤,因為(A1+A2)/(B1+B2)不可能被分解成A1/B1和A2/B2的形式。
一個反例見測試點2:
input#2
5 5
3 5 4 4 4
4 3 4 4 4
4 3 3 3 4
5 5 3 5 5
3 5 5 4 5
1 1 1 1 2
2 1 2 2 2
1 1 2 1 1
2 2 2 1 2
2 1 1 2 1
output#2
3.45454
這不是一個luo的坐標dp,如果不能dp,那還玩個毛線?(放棄)
其實我們仔細分析還是可以用dp的思路給出解答。
本題求的是:max(∑Ai/∑Bj)。設max (∑Ai/∑Bj)=k,即對於最優解, ∑Ai=k∑Bj
也就是Max k,k滿足 ∑(Ai-kBj)=0;
換句話說,因為Ai和Bj是給定的,只要對於某個答案k,得到上式=0,就可以斷定k是合法的答案。在此基礎上求出最大的合法的k。
怎樣確定答案k?
枚舉!只需二分k,再進行判斷即可。
二分k的過程中,如果k偏小,就會有 max(∑Ai/∑Bj)>k,也就是∑(Ai-kBj)>0 反之亦然。
也就是說我們每次二分答案的時候還是需要用到坐標dp的(套路深~~)
假設f[i,j]表示∑(Ai-kBj)的最大值(走到(i,j)時得到的價值的最大值)
for i:=1 to n do for j:=1 to m do f[i,j]:=max(f[i-1,j],f[i,j-1])+a[i,j]-k*b[i,j];
這個狀態轉移真心不難啊!!!
然後就可以愉快的二分了。(註意double二分格式,最後輸出l)
var n,m,i,j:longint; f:array[0..1000,0..1000]of double; a,b:array[1..1000,1..1000]of longint; l,r,mid:double; function max(a,b:double):double; begin if a>b then exit(a) else exit(b); end; function pd(k:double):boolean; var i,j:longint; begin for i:=1 to n do for j:=1 to m do f[i,j]:=max(f[i-1,j],f[i,j-1])+a[i,j]-k*b[i,j]; exit(f[n,m]>0); end; begin assign(input,‘sightseeing.in‘); assign(output,‘sightseeing.out‘); reset(input); rewrite(output); readln(n,m); for i:=1 to n do for j:=1 to m do read(a[i,j]); for i:=1 to n do for j:=1 to m do read(b[i,j]); for i:=1 to n do f[i,0]:=-1e300; for j:=1 to m do f[0,j]:=-1e300; f[0,1]:=0; l:=0; r:=1000; while r-l>1e-6 do begin mid:=(l+r)/2; if pd(mid)then l:=mid else r:=mid; end; writeln(l:0:5); close(input); close(output); end.
dp亂寫3:論dp在不在dp中(但在dp範疇)內的應用