几个经典的动态规划算法
一、動態(tài)規(guī)劃基本思想
? ? 一般來說,只要問題可以劃分成規(guī)模更小的子問題,并且原問題的最優(yōu)解中包含了子問題的最優(yōu)解,則可以考慮用動態(tài)規(guī)劃解決。動態(tài)規(guī)劃的實質(zhì)是分治思想和解決冗余,因此,動態(tài)規(guī)劃是一種將問題實例分解為更小的、相似的子問題,并存儲子問題的解而避免計算重復(fù)的子問題,以解決最優(yōu)化問題的算法策略。由此可知,動態(tài)規(guī)劃法與分治法和貪心法類似,它們都是將問題實例歸納為更小的、相似的子問題,并通過求解子問題產(chǎn)生一個全局最優(yōu)解。其中貪心法的當(dāng)前選擇可能要依賴已經(jīng)作出的所有選擇,但不依賴于有待于做出的選擇和子問題。因此貪心法自頂向下,一步一步地作出貪心選擇;而分治法中的各個子問題是獨立的 (即不包含公共的子子問題),因此一旦遞歸地求出各子問題的解后,便可自下而上地將子問題的解合并成問題的解。但不足的是,如果當(dāng)前選擇可能要依賴子問題的解時,則難以通過局部的貪心策略達(dá)到全局最優(yōu)解;如果各子問題是不獨立的,則分治法要做許多不必要的工作,重復(fù)地解公共的子問題。解決上述問題的辦法是利用動態(tài)規(guī)劃。該方法主要應(yīng)用于最優(yōu)化問題,這類問題會有多種可能的解,每個解都有一個值,而動態(tài)規(guī)劃找出其中最優(yōu)(最大或最小)值的解。若存在若干個取最優(yōu)值的解的話,它只取其中的一個。在求解過程中,該方法也是通過求解局部子問題的解達(dá)到全局最優(yōu)解,但與分治法和貪心法不同的是,動態(tài)規(guī)劃允許這些子問題不獨立,也允許其通過自身子問題的解作出選擇,該方法對每一個子問題只解一次,并將結(jié)果保存起來,避免每次碰到時都要重復(fù)計算。因此,動態(tài)規(guī)劃法所針對的問題有一個顯著的特征,即它所對應(yīng)的子問題樹中的子問題呈現(xiàn)大量的重復(fù)。動態(tài)規(guī)劃法的關(guān)鍵就在于,對于重復(fù)出現(xiàn)的子問題,只在第一次遇到時加以求解,并把答案保存起來,讓以后再遇到時直接引用,不必重新求解。
中的子問題呈現(xiàn)大量的重復(fù)。
動態(tài)規(guī)劃法的關(guān)鍵就在于,
對于重復(fù)出現(xiàn)的子問題,
只在第一次遇到時加以求解,
并把答案保存起來,
讓以后再遇到時直接引用,
不
必重新求解。
中的子問題呈現(xiàn)大量的重復(fù)。
動態(tài)規(guī)劃法的關(guān)鍵就在于,
對于重復(fù)出現(xiàn)的子問題,
只在第一次遇到時加以求解,
并把答案保存起來,
讓以后再遇到時直接引用,
不
必重新求解。
中的子問題呈現(xiàn)大量的重復(fù)。
動態(tài)規(guī)劃法的關(guān)鍵就在于,
對于重復(fù)出現(xiàn)的子問題,
只在第一次遇到時加以求解,
并把答案保存起來,
讓以后再遇到時直接引用,
不
必重新求解。
二、動態(tài)規(guī)劃實例
1、0-1背包問題
問題描述
? ? 假設(shè)我們有n件物品,分別編號為1,2,…n。其中編號為i的物品價值Vi,它的重量為Wi。為了簡化問題,假設(shè)價值和重量都是整數(shù)。現(xiàn)在假設(shè)我們有一個背包,它能夠承載的重量是W。現(xiàn)在,我們希望王背包里裝這些物品,使得包里裝的物品價值最大化,那么我們該如何選擇裝的物品?問題結(jié)構(gòu)如下圖所示:
初步分析
? ? 對于這個問題,一開始確實有點不太好入手。一堆的物品,每一個都有一定的質(zhì)量和價值,我們能夠裝入的總重量有限制,該怎么來裝使得價值最大呢?對于這n個物品,每個物品我們可能會選,也可能不選,那么我們總共就可能有2^n種組合選擇方式。如果我們采用這種辦法來硬算的話,則整體的時間復(fù)雜度就達(dá)到指數(shù)級別的,肯定不可行。
現(xiàn)在我們換一種思路。既然每一種物品都有價格和重量,我們優(yōu)先挑選那些單位價格最高的是否可行呢?比如在下圖中,我們有3種物品,他們的重量和價格分別是10, 20, 30 kg和60, 100, 120
? ? 那么按照單位價格來算的話,我們最先應(yīng)該挑選的是價格為60的元素,選擇它之后,背包還剩下50 - 10 = 40kg。再繼續(xù)前面的選擇,我們應(yīng)該挑選價格為100的元素,這樣背包里的總價值為60 + 100 = 160。所占用的重量為30, 剩下20kg。因為后面需要挑選的物品為30kg已經(jīng)超出背包的容量了。我們按照這種思路能選擇到的最多就是前面兩個物品。如下圖:
? ? 按照我們前面的期望,這樣選擇得到的價值應(yīng)該是最大的。可是由于有一個背包重量的限制,這里只用了30kg,還有剩下20kg浪費了。這會是最優(yōu)的選擇嗎?我們看看所有的選擇情況:
? ? 很遺憾,在這幾種選擇情況中,我們前面的選擇反而是帶來價值最低的。而選擇重量分別為20kg和30kg的物品帶來了最大的價值。看來,我們剛才這種選擇最佳單位價格的方式也行不通。
動態(tài)規(guī)劃解決問題
? 既然前面兩種辦法都不可行,我們再來看看有沒有別的方法。我們再來看這個問題。我們需要選擇n個元素中的若干個來形成最優(yōu)解,假定為k個。那么對于這k個元素a1, a2, …ak來說,它們組成的物品組合必然滿足總重量<=背包重量限制,而且它們的價值必然是最大的。因為它們是我們假定的最優(yōu)選擇嘛,肯定價值應(yīng)該是最大的。假定ak是我們按照前面順序放入的最后一個物品。它的重量為wk,它的價值為vk。既然我們前面選擇的這k個元素構(gòu)成了最優(yōu)選擇,如果我們把這個ak物品拿走,對應(yīng)于k-1個物品來說,它們所涵蓋的重量范圍為0-(W-wk)。假定W為背包允許承重的量。假定最終的價值是V,剩下的物品所構(gòu)成的價值為V-vk。這剩下的k-1個元素是不是構(gòu)成了一個這種W-wk的最優(yōu)解呢?
? ? 我們可以用反證法來推導(dǎo)。假定拿走ak這個物品后,剩下的這些物品沒有構(gòu)成W-wk重量范圍的最佳價值選擇。那么我們肯定有另外k-1個元素,他們在W-wk重量范圍內(nèi)構(gòu)成的價值更大。如果這樣的話,我們用這k-1個物品再加上第k個,他們構(gòu)成的最終W重量范圍內(nèi)的價值就是最優(yōu)的。這豈不是和我們前面假設(shè)的k個元素構(gòu)成最佳矛盾了嗎?所以我們可以肯定,在這k個元素里拿掉最后那個元素,前面剩下的元素依然構(gòu)成一個最佳解。
? ? 現(xiàn)在我們經(jīng)過前面的推理已經(jīng)得到了一個基本的遞推關(guān)系,就是一個最優(yōu)解的子解集也是最優(yōu)的。可是,我們該怎么來求得這個最優(yōu)解呢?我們這樣來看。假定我們定義一個函數(shù)c[i, w]表示到第i個元素為止,在限制總重量為w的情況下我們所能選擇到的最優(yōu)解。那么這個最優(yōu)解要么包含有i這個物品,要么不包含,肯定是這兩種情況中的一種。如果我們選擇了第i個物品,那么實際上這個最優(yōu)解是c[i - 1, w-wi] + vi。而如果我們沒有選擇第i個物品,這個最優(yōu)解是c[i-1, w]。這樣,實際上對于到底要不要取第i個物品,我們只要比較這兩種情況,哪個的結(jié)果值更大不就是最優(yōu)的么?
? ? 在前面討論的關(guān)系里,還有一個情況我們需要考慮的就是,我們這個最優(yōu)解是基于選擇物品i時總重量還是在w范圍內(nèi)的,如果超出了呢?我們肯定不能選擇它,這就和c[i-1, w]一樣。
? ? 另外,對于初始的情況呢?很明顯c[0, w]里不管w是多少,肯定為0。因為它表示我們一個物品都不選擇的情況。c[i, 0]也一樣,當(dāng)我們總重量限制為0時,肯定價值為0。
? ? 這樣,基于我們前面討論的這3個部分,我們可以得到一個如下的遞推公式:
? ? 有了這個關(guān)系,我們可以更進(jìn)一步的來考慮代碼實現(xiàn)了。我們有這么一個遞歸的關(guān)系,其中,后面的函數(shù)結(jié)果其實是依賴于前面的結(jié)果的。我們只要按照前面求出來最基礎(chǔ)的最優(yōu)條件,然后往后面一步步遞推,就可以找到結(jié)果了。
? ? 我們再來考慮一下具體實現(xiàn)的細(xì)節(jié)。這一組物品分別有價值和重量,我們可以定義兩個數(shù)組int[] v, int[] w。v[i]表示第i個物品的價值,w[i]表示第i個物品的重量。為了表示c[i, w],我們可以使用一個int[i][w]的矩陣。其中i的最大值為物品的數(shù)量,而W表示最大的重量限制。按照前面的遞推關(guān)系,c[i][0]和c[0][w]都是0。而我們所要求的最終結(jié)果是c[n][w]。所以我們實際中創(chuàng)建的矩陣是(n + 1) x (W + 1)的規(guī)格。
具體數(shù)據(jù):物品個數(shù)n = 5,物品重量w[n] = {0,2,2,6,5,4},物品價值v[n] = {0,6,3,5,4,6},W = 10。
代碼實現(xiàn)
[cpp] view plaincopyprint?
2、最大子數(shù)組和問題
問題
一個有N個整數(shù)元素的一維數(shù)組(A[0],A[1],…A[N-1]),這個數(shù)組有很多子數(shù)組,求子數(shù)組和的最大值?注意:子數(shù)組必須是連續(xù)的、不需要返回子數(shù)組的具體位置、數(shù)組中包含:正、負(fù)、零整數(shù)、子數(shù)組不能空。
例如:
int A[5] = {-1,2,3,-4,2};
符合條件的子數(shù)組為2,3,即答案為5;
窮舉法:
[cpp] view plaincopyprint?窮取法最為直接,當(dāng)然耗時也較多,時間復(fù)雜度為O(n^2);
進(jìn)一步分析
我們利用窮舉法雖然簡單易懂,但是其時間復(fù)雜度很大,我們試著優(yōu)化。現(xiàn)在考慮數(shù)組的第一個元素A[0],與和最大的子數(shù)組(A[i],……A[j])之間的關(guān)系,有以下三種關(guān)系:
?1) i = j = 0;A[0]本身構(gòu)成和最大的子數(shù)組
?2) j > i = 0;和最大的子數(shù)組以A[0]開頭
?3) i > 0;A[0]與和最大子數(shù)組沒有關(guān)系
從上面3中情況可以看出,可以將一個大問題(N個元素的數(shù)組)轉(zhuǎn)化為一個較小的問題(N - 1個元素的數(shù)組)。假設(shè)已經(jīng)知道(A[1],……A[N-1])中和最大的子數(shù)組和為MaxSum[1],并且知道,(A[1],……A[N-1])中包含A[1]的和最大的子數(shù)組為TempMaxSum[1]。我們就可以把(A[0],……A[N-1])求和最大子數(shù)組問題轉(zhuǎn)換為,MaxSum[0] = max{A[0],A[0]+TempMaxSum[1],MaxSum[1]}。
代碼實現(xiàn):
[cpp] view plaincopyprint?
總結(jié)
以上是生活随笔為你收集整理的几个经典的动态规划算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python截取视频_python使用f
- 下一篇: 安全多方计算之GMW协议