演算法導論 讀書筆記 動態規劃

2021-09-13 03:22:05 字數 2735 閱讀 6900

一. 動態規劃介紹

動態規劃與分治法整體思路相近:組合子問題的解求解原問題。將問題劃分為互不相交的子問題,遞迴地求解子問題,再將它們的解組合起來,求出原問題的解。

動態規劃的應用場景:子問題重疊。子問題的解決需要解決子子問題(遞迴),而不同子問題之間的子子問題是相同的。

動態規劃的實現及特點:對於分治演算法而言,它只是把問題分解為幾個子問題,再分別求解這些子問題,而在這些子問題的解決中,會把它分解為子子問題,解決每乙個子子問題,由於這些子子問題很多都是重複的,因而每次都重新計算會浪費計算資源。而對於動態規劃而言,它對於每個子子問題只求解一次,將問題的解儲存下來,在再一次遇見這些子子問題的時候,就可以直接呼叫這些子子問題的解,而不用重新計算。故動態規劃能通過儲存中間子子問題的解來降低演算法複雜度,而額外的空間往往只需要乙個**/陣列就可以。

動態規劃應用的常見問題:最優化問題,對某個場景尋求最優解(最大/最小值)。

二.動態規劃演算法

步驟:1.刻畫乙個最優解的結構特徵

2.遞迴地定義最優解的值

3.計算最優解的值,

4.利用計算出的資訊構造乙個最優解

三.舉例

1.鋼條切割

問題介紹:

假定我們知道一家公司**一段長度為i 的鋼條的**為pi(i=1,2,...),鋼條的長度為整數,下圖是**表

長度i123

4567

8910**pi15

891017

1720

2430

鋼條切割問題:給定一段長度為n的鋼條和乙個**表pi(i=1,2,...n),求切割鋼條方案,使得銷售收益rn最大,若長度為n的鋼條的pn足夠大,最優解可能完全不需要切割。

問題分析:

假設有n=4,有三個切割位置1-2,2-3,3-4,以1,0編碼它們是否切割,得到以下切割方案,對應於0-7的二進位製碼,有以下8種切割方案:

a. 4 (不切割);b. 3 1;c. 2 2;d. 2 1 1;

e. 1 3 ;f. 1 2 1 ;g. 1 1 2;h. 1 1 1 1;

很顯然,最有解應當是c,r=5+5=10。

對於長度為n的鋼條,它的切割位置有n-1個,可以選擇切割或者不切割,故它的切割方案有2^(n-1)。

動態規劃演算法應用分析:

第一步,刻畫乙個最優解的結構特徵。

也就是說我們要先假設已經得到了最優解,那麼針對這道題,我們已經得到了分割後的幾段鋼條,得到了幾個最優切割位置,那麼我們可以考慮選擇乙個最優切割位置切割,然後得到兩個子鋼條(每個子鋼條不止一段),而每個子鋼條都是經最優切割位置切割好的。而每個子鋼條就相當於原鋼條,最後把每個切割位置都切割後,問題就解決了。所以我們的最優解的結構特徵就是:每個子鋼條都是經最優切割位置切割好的。

好我們重頭再來一遍,這次我們要加上具體的實現操作,我們選擇乙個最優切割位置切割,那麼如何選擇乙個最優切割位置切割呢,最簡單的方法,就是把每個位置都切割一遍,看看哪個最優。所以我們有公式

注:pn就表示不切割的情況,共有n-1個切割位置。

在計算中,這個遞迴表示式的終止條件是n=1,此時r1=p1

實際上相比於每次將乙個原問題分為兩個問題,還有一種更簡單的方式,就是我們每次切割的時候,保證切割的其中一條子鋼條只有一段,而另一條子鋼條是經最優切割位置切割好的。,而不僅僅是切為兩個子鋼條。於是公式可以簡化為

這樣原問題的最優解只包含乙個相關子問題,而不是兩個。

直接遞迴 c++**

/*p[0]儲存p1值*/

cut_rod(p,n)

return q;

}

但這樣做是不夠的,我們注意到,雖然我們是將乙個原問題分解為乙個子問題,但是在for迴圈中,每個子問題都要計算一次,長度為n的問題要呼叫n個長度分別為0,1,2,...,n-1的子問題,遞迴次數

當i=4時,有

n4=1+n3+n2+n1+n0=1+(1+n2+n1+n0)+n2+n1+n0=2(1+n2+n1+n0)=2(1+(1+n1+n0)+n1+n0)=4(1+n1+n0)=4(1+(1+n0+n0)=8(n0+1)=16

於是總共呼叫次數為2^n-1,它的複雜度為o(2^n),是無法求解的。

於是,我們來思考切割鋼條的子子問題:子鋼條到底有幾種呢?僅有n種長度的子鋼條,也就是說在遞迴求解子問題的時候一直在重複計算這些子子問題,這才使得運算效率變得極低,事實上只需要把這n種情況全部計算一遍,就可以得到最終的解。

動態規劃演算法,就是計算這些子子問題的解,再組合這些子子問題的解,得到最優解。

主要有兩種實現方式,第一種是自頂而下的方式,在上面所講的遞迴方式中加入備忘機制,使得每個子子問題在計算了一次之後記錄下來,第二次遇見的時候就可以直接呼叫

自頂而下 c++**

/*p[0]儲存p1值*/

memoized_cut_rod(int *p,int n)

r[j]=q;

}return r[n];

}

自底而上是將子子問題,即子鋼條長度按從小到大依次求解,計算i規模的子問題依賴比i更小規模的子問題。

拓展:如果問題再變得複雜一點,鋼條的售價不僅僅依賴於它的長度,而且還依賴於它的位置,也即p(i,j),i,j分別表示初始結束位置,切割位置依舊只能選擇整數字置。

那麼對於這個新問題,它的子問題依舊是切割一次後的子鋼條,而它的子子問題就變成了每一種可能的子鋼條情況,不再是簡單的長度從1到n,而是要首尾各排列組合,也就是n(n+1)/2種情況,在這個時候,我們在使用動態規劃的時候,就需要用乙個二維陣列來儲存這n(n+1)/2個數值。

四. 參考文獻

1.《演算法導論》

讀書筆記 演算法導論

第2章演算法入門 浮於表面不如深入其中,送給自己,自己是最大的敵人,那麼就盡最大努力去克服自己,沉思,冷靜,不浮躁!勘誤 在演算法導論第9頁,扼要的扼 內容提要 1 偽 的表示方法 2 插入排序演算法分析 3 迴圈不變式 4 演算法設計之分治法 divide and conquer 5 合併排序演算...

演算法導論 讀書筆記2010 12 6

演算法就是一系列的計算步驟,用來將輸入資料轉換為輸出結果。資料結構師儲存和組織資料的一種方式,以便於對資料進行訪問和修改。插入排序演算法,對n個資料項的時間大約是c1n 2,其中c1是乙個不依賴於n的常量。亦即該演算法所需的時間大致與n 2成正比。合併排序演算法,排序n個資料項的時間大約是c2log...

《演算法導論》讀書筆記(一)

理解 輸入到輸出的計算過程稱為演算法。1.演算法描述 2.證明演算法正確性 3.分析演算法效率 兩個例子 1.插入排序 思想 從未排序的序列中取出乙個元素,將其插入到已排序序列的正確位置。實現 include include using namespace std int main for int ...