記憶化搜索的應用

記憶化搜索的應用

通常來講,動態規劃總要遍歷全部的狀態,而搜索能夠排除一些無效狀態。更重要的是搜索還能夠剪枝,可能剪去大量沒必要要的狀態,所以在空間開銷上每每比動態規劃要低不少。算法

如何協調好動態規劃的高效率與高消費之間的矛盾呢?有一種折中的辦法就是記憶化算法。記憶化算法在求解的時候仍是按着自頂向下的順序,每求解一個狀態,就將它的解保存下來,之後再次遇到這個狀態的時候,就沒必要從新求解了。這種方法綜合了搜索和動態規劃兩方面的優勢,於是仍是頗有使用價值的。數組

舉一個例子:如右圖所示是一個有向無環圖,求從頂點1到頂點6的最長路徑。(規定邊的方向從左到右)函數

咱們將從起點(頂點1)開始到某個頂點的最長路徑做爲狀態,用一維數組opt記錄。Opt[j]表示由起點到頂點j時的最長路徑。顯然,opt[1]=0,這是初始狀態,即動態規劃的邊界條件。因而,咱們很容易地寫出狀態轉移方程式:opt[j]=max{opt[k]+a[k,j]}(k到j有一條長度爲a[k,j]的邊)。雖然有了完整的狀態轉移方程式,可是仍是不知道動態規劃的順序。因此,還須要先進行一下拓撲排序,按照排序的順序推下去,opt[6]就是問題的解。spa

能夠看出,動態規劃相比搜索之因此高效,是由於它將全部的狀態都保存了下來。當遇到重複子問題時,它不像搜索那樣把這個狀態的最優值再計算一遍,只要把那個狀態的最優值調出來就能夠了。例如,當計算opt[4]和opt[5]時,都用到了opt[3]的值。由於已經將它保存下來了,因此就沒有必要再去搜索了。code

可是動態規劃仍然是有缺點的。一個很突出的缺點就是要進行拓撲排序。這道題的拓撲關係是很簡單的,但有些題的拓撲關係是很複雜的。對於這些題目,若是也進行拓撲排序,工做量很是大。遇到這種狀況,咱們能夠用記憶化搜索的方法,避免拓撲排序。blog

【例】滑雪排序

【問題描述】遞歸

小明喜歡滑雪,由於滑雪的確很刺激,但是爲了得到速度,滑的區域必須向下傾斜,當小明滑到坡底,不得再也不次走上坡或等着直升機來載他,小明想知道在一個區域中最長的滑坡。滑坡的長度由滑過點的個數來計算,區域由一個二維數組給出,數組的每一個數字表明點的高度。下面是一個例子:it

1     2     3     4     5io

16    17    18    19    6

15    24    25    20    7

14    23    22    21    8

13    12    11    10    9

一我的能夠從某個點滑向上下左右相鄰四個點之一,當且僅當高度減少,在上面的例子中,一條可行的滑坡爲25-24-17-16-1(從25開始到1結束),固然25-24……2…1更長,事實上這是最長的一條。

【輸入格式】

輸入的第一行爲表示區域的二維數組的行數R和列數C(1≤R、C≤100),下面是R行,每行有C個數表明高度。

【輸出格式】

輸出區域中最長的滑坡長度。

【輸入樣例】ski.in

5      5

1      2     3     4     5

16    17    18    19    6

15    24    25    20    7

14    23    22    21    8

13    12    11    10    9

【輸出樣例】ski.out

25

【算法分析】

因爲一我的能夠從某個點滑向上下左右相鄰四個點之一,如上圖所示。當且僅當高度減少,對於任意一個點[i,j],當它的高度小於與之相鄰的四個點([i-1,j], [i,j+1], [i+1,j], [i,j-1])的高度時,這四個點能夠滑向[i,j],用f[i,j]表示到[i,j]爲止的最大長度,則f[i,j]=max{f(i+a,j+b)}+1,其中座標增量{(a,b)=[(1,0),(-1,0),(0,1),(0,-1)],0<i+a<=r,0<j+b<=c,High[i,j]<High[i+a,j+b]}。爲了保證知足條件的f[i+a,j+b]在f[i,j]前算出,須要對高度排一次序,而後從大到小規劃(高度)。最後再比較一下全部f(i,j){0<i≤r,0<j≤c},找出其中最長的一條路線。咱們還能夠用記憶化搜索的方法,它的優勢是不需進行排序,按照行的順序,利用遞歸逐點求出區域中到達此點的最長路徑,每一個點的最長路徑只求一次。

 1 const
 2   dx:array[1..4] of shortint=(0,-1,0,1);    {x的座標增量}
 3   dy:array[1..4] of shortint=(-1,0,1,0);    {y的座標增量}
 4 var
 5  r,c,ans,anss:longint;  6   map,f:array[1..100,1..100] of longint;  7 procedure init;  8 var i,j:longint;  9 begin
10  readln(r,c); 11   for i:=1 to r do
12    for j:=1 to c do
13     read(map[i,j]);                   {讀入每一個點的高度}
14   ans:=0; anss:=0; 15   fillchar(f,sizeof(f),0); 16 end; 17 function search(x,y:longint):longint;           {函數的做用是求到[x,y]點的最長路徑}
18 var i,j,nx,ny,tmp,t:longint; 19 begin
20   if f[x,y]>0 then   {此點長度已經求出,沒必要進行進一步遞歸,保證每個點的最大長度只求一次,這是記憶化搜索的特色}
21    begin
22      search:=f[x,y]; exit; 23    end; 24   t:=1; 25   for i:=1 to 4 do                      {從四個方向上搜索能達到[x,y]的點}
26    begin
27      nx:=x+dx[i]; ny:=y+dy[i];               {新座標}
28      if (1<=nx)and(nx<=r) and (1<=ny)and(ny<=c)    {邊界限制}
29                       and (map[nx,ny]>map[x,y])    {高度比較}
30       then
31        begin
32          tmp:=search(nx,ny)+1;              {遞歸進行記憶化搜索}
33          if tmp>t then t:=tmp; 34        end; 35    end; 36   f[x,y]:=t; 37   search:=t; 38 end; 39 procedure doit; 40 var i,j:longint; 41 begin
42   for i:=1 to r do               {按照行的順序,利用遞歸逐點求出區域中到達此點的最長路徑}
43    for j:=1 to c do
44      begin
45        anss:=search(i,j); 46        //f[i,j]:=anss; 47        if anss>ans then ans:=anss;          {尋找最大長度值}
48      end; 49 end; 50 procedure outit; 51 var i,j:longint; 52 begin
53   {for i:=1 to r do begin 54  for j:=1 to c do 55  write(f[i,j],' '); writeln; end;}
56  writeln(ans); 57 end; 58 begin
59  init; 60  doit; 61  outit; 62 end.
相關文章
相關標籤/搜索