動態規劃適合求解最優問題,比如最大值最小值等。它可以顯著的降低時間複雜度,提高**的執行效率。
0-1 揹包問題
在上篇總結中,用回溯演算法解決了 0-1揹包問題。但是,在求解的過程中,我們應該能想象的出,有些步驟是一直在重複執行。如果揹包的總載重為 9 ,物品個數為 5 ,質量分別為 [2,2,4,6,3]。那麼將這些資料帶入回溯演算法的**中,執行階段用遞迴樹來表示:
在上圖中遞迴樹中每個節點的狀態,用 (i,cw) 來表示。i 表示要決策第幾個物品是否放入揹包,cw 代表當前揹包的總質量。例如 (2,2) 代表第二個物品將決定是否放入揹包,決策之前揹包的總質量是 2。
通過上面的圖例加說明,我們可以看出 f(2,2) 和 f(3,4) 都被重複計算了兩次。為了減少重複計算的次數,我們可以把計算過的情況記錄在案,當下次遇到相同的計算的時候,直接取值用就好。**如下:
// 儲存揹包中物品總重量的最大值
public
int maxw = integer.min_value;
private
int[
] weight =
;// 物品重量
private
int n =5;
// 物品個數
private
int w =9;
// 揹包承受的最大重量
private
boolean
mem =
newboolean[5
][10]
;// 備忘錄,預設值 false
/** *
* @param i 表示任務進行的階段,考察到哪個物品了
* @param cw 表示當前已經裝進去的物品的重量和
* @param items 陣列存放每個物品的質量
*/public
voidf(
int i,
int cw)
return;}
if(mem[i]
[cw]
) mem[i]
[cw]
=true
;// 記錄 (i, cw) 這個狀態
//該物品不放入揹包
f(i +
1, cw)
;//該物品放入揹包,物品放入揹包的時候,需要判斷當前物品總質量是否小於等於揹包的承載重量
if(cw + weight[i]
<= w)
}
上面的過程理解之後,接下來總結動態規劃如何實現上面的過程。
求解的過程分解成 n 個階段,n 代表物品的數量。每個階段,決定乙個物品是否放入,決策之後,揹包的重量會發生變化,這個變化的狀態對應到遞迴樹中就是不同的節點。
現在,我們把遞迴樹中每一層中相同狀態的節點合併,只留下不同狀態的集合,並且在本次狀態集合下去推導下一次的狀態集合。由於我們值儲存不相同的狀態,並且總重量不能大於 w (w 代表總重量),所以每層的狀態集合最多有 w 個。(此處補充一下:前提是物品的質量都是整數,不可分割。)這樣我們就避免了每一層的結果整合指數增長。
針對每一層的狀態集,建立乙個 states[n][w + 1] 二維陣列來儲存。n 代表層級,w + 1 代表結果集區間是從 0 到 w + 1。
對於放入第 0 個物品,質量為 2 ,對應陣列中就是 states[0][0] = true,states[0][2] = true。states[0][0] 代表沒有放入,states[0][2] 代表放入。以此類推,整個過程就是如下圖所示:
**如下:
// weight: 物品重量,n: 物品個數,w: 揹包可承載重量
public
intknapsack
(int
weight,
int n,
int w)
}// 把第 i 個物品放入揹包
// w - weight[i] 代表該物品放入之後,只有 j 以及 j 前面的不超重
for(
int j =
0; j <= w - weight[i]
;++j)}}
for(
int i = w; i >=0;
--i)
return0;
}
上述**中有詳細的注釋,足以看懂整個過程。
上面所舉的例子以及**的實現,就是乙個動態規劃演算法的實現。將問題分階段解決,每個階段都有不同的結果集,我們合併相同的結果集,然後用該結果集去推到下一階段的結果集,這就是乙個動態規劃的過程。
當然,上述**雖然比起回溯演算法提高了效率,但是卻需要額外開闢乙個二維陣列,增加可空間的消耗。下面用**實現一種用一維陣列完成的動態規劃:
public
static
intknapsack2
(int
items,
int n,
int w)}}
for(
int i = w; i >=0;
--i)
return0;
}
上述**之所以可以用一維陣列實現,主要是省略了每一層中物品不放入揹包的邏輯。如果不省略,進行了不放入揹包的判斷,其實只是把上一次的狀態搬移到本層。但是這次搬移其實是無用功的,因為上一層已經滿足的條件,搬移到這一整層,依舊是滿足條件的。所以,有意義的操作就是對本層物品放入的判斷。另外乙個原因,因為是動態規劃,我們都是用上一層的結果集推導下一層的結果集。也就是說,當本層的結果集推導出來後,上一層的結果集已經沒有用處了。所以,我們才可以採用本層額結果集覆蓋上層的結果集,最終完成動態規劃。
總結初入演算法學習,必是步履蹣跚,一路磕磕絆絆跌跌撞撞。看不懂別慌,也別忙著總結,先讀五遍文章先,無他,唯手熟爾~
與諸君共勉
演算法筆記 動態規劃 1
求解方法 標準的01揹包問題是指,有n int型 個物品和最多裝重量w int型 的揹包。weight陣列表示物品的重量,即weight i 表示第i個物品的重量 value陣列表示物品的價值,即value i 表示第i個物品的價值。問把哪些物品裝入揹包使得物品價值總和最大,每個物品只能裝一次。舉例...
演算法 動態規劃(1)
把問題拆分成若干個子問題,類似遞迴 分治 但是動規多用於處理最優解,有重疊子問題的問題,因為動態規劃對於重疊子問題不會反覆計算,會建立一張表將之前計算過的子問題答案直接儲存,避免了重複計算,加快計算速度 練習1 有8個任務,每個任務完成需要一定的時間,完成之後就會有相應的報酬 圖上的紅色字段 但是任...
演算法1 動態規劃
動態規劃的步驟是 1.找出最優解的特徵,並刻畫其結構特徵 2.遞迴地定義最優值 3.以自底向上的方式計算出最優值 4.根據計算最優值的時得到的資訊,構造最優解。動態規劃演算法的有效性依賴於兩個最重要的性質 最優子結構性質和重疊子問題性質。栗子1,矩陣連乘問題。1 動態規劃 矩陣連乘問題 2 incl...