1. 程式人生 > >dp亂寫3:論dp在不在dp中(但在dp範疇)內的應用

dp亂寫3:論dp在不在dp中(但在dp範疇)內的應用

經典的 exit e30 reset 最大值最小值 二叉樹 錯誤 出口 解答

最近正兒八經的學習了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啦。

但是問題來了,不是每一題符合從前推到後且n,m比較小的題目就是dp。
但是用dp思想一定沒錯啦。。。
下面有個小例子來闡述一下p在不在dp中(但在dp範疇)內的應用(簡稱dp的應用)
【題目名稱】遊覽(sightseeing.pas/c/cpp)
【題目描述】周末到了,gnocuil打算去逛遊樂園。他所去的遊樂園可以看做一個N*M的網格,每個網格上都是一個景點。遊樂園的入口在(1,1),出口在(N,M)處。遊樂園有一個不成文的規定:遊覽時,只能朝著坐標增大的方向走,因為如果坐標減小,會很不吉利。換句話說,每次只能向右方、正下方走。當然,只可以走到相鄰的景點,不可以沿著斜線走。
對於走到的每個景點,gnocuil都會得到一個快樂度A[i,j]。同時,遊覽會使他疲倦,每個景點都會使他增加B[i,j]的疲勞度。
gnocuil認為,對於每次遊覽,用總快樂度除以總疲勞度,得到的就是這次遊覽的價值。請你設計一個方案,使得對於給定的遊樂園信息,遊覽的價值最大。
【輸入文件】第一行是兩個整數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範疇)內的應用