多階段決策過程(multistep decision process)是指這樣一類特殊的活動過程,過程能夠按時間順序分解成若干個相互聯繫的階段,在每個階段都須要作出決策,所有過程的決策是一個決策序列。動態規劃(dynamic programming)算法是解決多階段決策過程最優化問題的一種經常使用方法,難度比較大,技巧性也很強。利用動態規劃算法,能夠優雅而高效地解決不少貪婪算法或分治算法不能解決的問題。動態規劃算法的基本思想是:將待求解的問題分解成若干個相互聯繫的子問題,先求解子問題,而後從這些子問題的解獲得原問題的解;對於重複出現的子問題,只在第一次遇到的時候對它進行求解,並把答案保存起來,讓之後再次遇到時直接引用答案,沒必要從新求解。動態規劃算法將問題的解決方案視爲一系列決策的結果,與貪婪算法不一樣的是,在貪婪算法中,每採用一次貪婪準則,便作出一個不可撤回的決策;而在動態規劃算法中,還要考察每一個最優決策序列中是否包含一個最優決策子序列,即問題是否具備最優子結構性質。
動態規劃算法的有效性依賴於待求解問題自己具備的兩個重要性質:最優子結構性質和子問題重疊性質。
一、最優子結構性質。若是問題的最優解所包含的子問題的解也是最優的,咱們就稱該問題具備最優子結構性質(即知足最優化原理)。最優子結構性質爲動態規劃算法解決問題提供了重要線索。
二、子問題重疊性質。子問題重疊性質是指在用遞歸算法自頂向下對問題進行求解時,每次產生的子問題並不老是新問題,有些子問題會被重複計算屢次。動態規劃算法正是利用了這種子問題的重疊性質,對每個子問題只計算一次,而後將其計算結果保存在一個表格中,當再次須要計算已經計算過的子問題時,只是在表格中簡單地查看一下結果,從而得到較高的解題效率。
當咱們已經肯定待解決的問題須要用動態規劃算法求解時,一般能夠按照如下步驟設計動態規劃算法:
一、分析問題的最優解,找出最優解的性質,並刻畫其結構特徵;
二、遞歸地定義最優值;
三、採用自底向上的方式計算問題的最優值;
四、根據計算最優值時獲得的信息,構造最優解。
1~3步是動態規劃算法解決問題的基本步驟,在只須要計算最優值的問題中,完成這三個基本步驟就能夠了。若是問題須要構造最優解,還要執行第4步;此時,在第3步一般須要記錄更多的信息,以便在步驟4中,有足夠的信息快速地構造出最優解。
下面經過對具體實例的分析,幫助你們領會可用動態規劃算法求解的問題應具備的兩個性質以及動態規劃算法的設計思想。
例:攔截導彈(問題來源:1999年全國青少年信息學(計算機)奧林匹克分區聯賽高中組複賽試題第一題)
某國爲了防護敵國的導彈襲擊,發展出一種導彈攔截系統。可是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈可以到達任意的高度,可是之後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。因爲該系統還在試用階段,因此只有一套系統,所以有可能不能攔截全部的導彈。
輸入導彈依次飛來的高度(雷達給出的高度數據是不大於30000 的正整數),計算這套系統最多能攔截多少導彈,並依次輸出被攔截的導彈飛來時候的高度。
樣例:
INPUT
389 207 155 300 299 170 158 65
OUTPUT
6(最多能攔截的導彈數)
389 300 299 170 158 65
分析:由於只有一套導彈攔截系統,而且這套系統除了第一發炮彈能到達任意高度外,之後的每一發炮彈都不能高於前一發炮彈的高度;因此,被攔截的導彈應該按飛來的高度組成一個非遞增序列。題目要求咱們計算這套系統最多能攔截的導彈數,並依次輸出被攔截導彈的高度,實際上就是要求咱們在導彈依次飛來的高度序列中尋找一個最長非遞增子序列。
設X={x1,x2,…,xn}爲依次飛來的導彈序列,Y={y1,y2,…,yk}爲問題的最優解(即X的最長非遞增子序列),s爲問題的狀態(表示導彈攔截系統當前發送炮彈可以到達的最大高度,初值爲s=∞——第一發炮彈可以到達任意的高度)。若是y1= x1,即飛來的第一枚導彈被成功攔截。那麼,根據題意「每一發炮彈都不能高於前一發的高度」,問題的狀態將由s=∞變成s≤x1(x1爲第一枚導彈的高度);在當前狀態下,序列Y1={y2,…,yk}也應該是序列X1={x2,…,xn}的最長非遞增子序列(你們用反證法很容易證實)。也就是說,在當前狀態s≤x1下,問題的最優解Y所包含的子問題(序列X1)的解(序列Y1)也是最優的。這就是攔截導彈問題的最優子結構性質。
設D(i) 爲第i枚導彈被攔截以後,這套系統最多還能攔截的導彈數(包含被攔截的第i枚)。咱們能夠設想,當系統攔截了第k枚導彈xk,而xk又是序列X={x1, x2,…,xn}中的最小值,即第k枚導彈爲全部飛來的導彈中高度最低的,則有D(k)=1;當系統攔截了最後一枚導彈xn,那麼,系統最多也只能攔截這一枚導彈了,即D(n)=1;其它狀況下,也應該有D(i)≥1。根據以上分析,可概括出問題的動態規劃遞歸方程爲:
假設系統最多能攔截的導彈數爲dmax(即問題的最優值),則
dmax (i爲被系統攔截的第一枚導彈的順序號)
因此,要計算問題的最優值dmax,須要分別計算出D(1)、D(2)、……D(n)的值,而後將它們進行比較,找出其中的最大值。根據上面分析出來的遞歸方程,咱們徹底能夠設計一個遞歸函數,採用自頂向下的方法計算D(i)的值。而後,對i從1到n分別調用這個遞歸函數,就能夠計算出D(1)、D (2)、……D(n)。但這樣將會有大量的子問題被重複計算。好比在調用遞歸函數計算D(1)的時候,可能須要先計算D(5)的值;以後在分別調用遞歸函數計算D(2)、D(3)、D(4)的時候,都有可能須要先計算D(5)的值。如此一來,在整個問題的求解過程當中,D(5)可能會被重複計算不少次,從而形成了冗餘,下降了程序的效率。
其實,經過以上分析,咱們已經知道:D(n)=1。若是將n做爲階段對問題進行劃分,根據問題的動態規劃遞歸方程,咱們能夠採用自底向上的方法依次計算出D(n-1)、D(n-2)、……D(1)的值。這樣,每一個D(i)的值只計算一次,並在計算的同時把計算結果保存下來,從而避免了有些子問題被重複計算的狀況發生,提升了程序的效率。算法代碼以下:
for i=1 to n
D(i)=1
next i
for i=n-1 to 1 step -1
for j=i+1 to n
if x(j)<=x(i) and D(i)<D(j)+1 then
D(i)=D(j)+1
endif
next j
next i
dmax=0:xh=1
rem dmax用來保存問題的最優值(系統最多能攔截的導彈數);xh用來保存第一枚被攔截的導彈的順序號,以便後面構造最優解
for i=1 to n
if D(i)>dmax then
dmax=D(i)
xh=i
endif
next i
咱們在計算最優值時保存了被攔截的第一枚導彈的順序號xh。接下來經過這個信息構造問題的最優解(由全部被攔截的導彈的高度組成的非遞增序列)。算法代碼以下:
print x(xh)
for i=xh+1 to n
if x(i)<=x(i-1) then
print x(i)
endif
next i
結束語:動態規劃算法和貪婪算法都是構造最優解的經常使用方法。動態規劃算法沒有一個固定的解題模式,技巧性很強。能夠說,動態規劃算法在實際生活中的每一次應用都是一種創造。你們要多練習,多實踐,從實踐中領會動態規劃算法的精髓,不斷提升本身的應用能力算法