動態規劃,菜鳥的一點心得

2021-09-24 12:12:00 字數 3199 閱讀 1690

動態規劃(dynamic programming),簡稱dp演算法,是一種通過把原問題分解為相對簡單的子問題的方式來求解複雜問題的方法。對於乙個問題的某個階段,在這個階段以後的發展過程中都不受這階段以前各段狀態的影響,這樣的問題通常能夠用動態規劃的思想去解決。

以下舉乙個簡單例子:

計算sum(n) = 1 + 2 + 3 + 4 + … + n

假設n = 9,sum(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45,

再假設n = 10,顯而易見sum(10) = sum(9) + 10 = 55,

由此可見,倘若已知sum(n - 1)的值,並記錄下來,sum(n)的值就能立刻

用sum(n) = sum(n - 1) + n計算出來。

其中,sum(n)為原問題,sum(n - 1)為子問題。若此前不做任何準備,讓你計算sum(100),可能需要花費大量時間;而若此前已知sum(99)的值,你總是 能快速知道sum(100)的值。

這就是動態規劃的核心思想,將原問題分解成子問題,並記住已經解決過的子問題的解,從而求解複雜問題。

能用動態規劃求解問題,常常具備以下特點

1、計數

有多少種方式走到右下角

有多少種方法選出k個數使得和是sum

2、求最大最小值

從左上角走到右下角路徑的最大數字和

最長上公升子串行長度

3、求存在性

取石子遊戲,先手是否必勝

能不能選出k個數使得和是sum

動態規劃求解常常包括以下幾個步驟:

1、確定狀態

常常在求解動態規劃時需要開乙個陣列dp[i]或者是dp[i][j],確定狀態就是要明確dp[i]或是dp[i][j]的每個元素代表的實際含義是什麼。

2、確定狀態轉移方程

動態規劃中乙個階段的狀態總是上一階段狀態和上一階段決策的結果,使用乙個函式來表示前後階段關係的方程,稱為狀態轉移方程。在動態規劃中,確定狀態轉移方程是最為關鍵的一步。

通常確定狀態轉移方程時,需要明確兩個問題:

1)最後乙個決策

確定什麼是最後乙個決策,以及最後乙個決策如何由上乙個決策轉移得到,並且確保前面

的決策總是最優解。

1)子問題

子問題和原問題幾乎一樣,只是子問題的規模小於原問題。

3、確定初始條件和邊界情況

狀態轉移方程適用於整個求解過程,但常常不滿足最開始的情況,求解時總是需要根據初始條件初始化狀態轉移方程。

另外,與遞迴類似,在使用狀態轉移方程時,需要確定邊界情況。

題目:有n個重量和價值分別為wi和vi的物品。從這些物品中挑選出總重量不超過w的物品,求所有挑選方案中價值總和的最大值。

限制條件:

1 <= n <= 100

1 <= w, i <= 100

1 <= w <= 10000

求解思路如下:

一開始,用w[i]和v[i]陣列順序存放對應的物品重量和價值

接著,運用動態規劃的思想來解決問題

1、確定狀態

我們應該使用dp[i]陣列的形式還是使用dp[i][j]陣列的形式呢?

可以以這樣的思維來考慮:

現實生活中在真正挑選時,總是以這樣的過程來進行:拿起第n個物品,接著決定是否將該物品放入揹包中。

那麼,我們揹包中的狀態總是由第n-1次挑選物品時的揹包狀態在決策後轉移而來的。而揹包狀態時揹包的重量,即轉移方程包括了兩個轉移的條件,如此,使用dp[i][j]比較合理。

接著需要確定dp[i][j]的每個元素代表的實際意義是什麼。

顯而易見,i是挑選第i個物品,j是當前揹包裝載的最大重量,而dp[i][j]是挑選第i個物品時,總重量不超過j的最大物品價值。

2、確定狀態轉移方程

對於dp[i][j],考慮現實意義,在挑選物品時,需要對挑選與不挑選進行決策,總是有以下情況:

1)第i個物品的重量大於j,不挑選第i個物品。

2)第i個物品的重量小於等於j

a)挑選第i個物品使揹包到達j重量後的總價值比不挑選第i個物品使揹包到達j重量的總價

值來得低,不挑選第i個物品。

b)挑選第i個物品使揹包到達j重量後的總價值比不挑選第i個物品使揹包到達j重量的總價

值來得高,挑選第i個物品。

**實現如下:

if(j < w[i])

dp[i][j] = dp[i - 1][j];

else

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

3、確定初始條件和邊界情況

初始條件有倆,乙個是i = 0的情況,乙個是j = 0的情況

對於i = 0,即挑選第0個物品時,很明顯,總價值總是為0

對於j = 0,即揹包重量為0時,也很明顯,總價值也總是為0

為了方便,在初始化時使用memset將dp[i][j]陣列歸零。

memset(dp, 0, sizeof(dp));
4、完整**

memset(dp, 0, sizeof(dp));

for(int i = 1; i <= n; i++)

for(int j = 0; j <= m; j++)

if(j < w[i])

dp[i][j] = dp[i - 1][j];

else

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

5、演算法改進

上面的轉移方程為dp[i][j]的形式,實際上該問題能夠使用dp[j]的形式來進行簡化

具體**如下

memset(dp, 0, sizeof(dp));

for(int i = 1; i <= n; i++)

for(int j = m; j >= w[i]; j--)

dp[j] = max(dp[j], dp[j - w[i]] + v[i]);

對於dp[i][j]而言,當在挑選第i個物品時,只有第i-1行的資料需要被使用到,並最終只需保留第i行的決策資料,供挑選第i+1個物品時使用。

而對於dp[j]而言,在挑選第i個物品時,從j=w的情況開始挑選,這樣陣列中dp[0]到dp[w-1]的資料儲存的仍然是挑選完第i-1個物品後的決策資料,同時又能更新挑選第i個物品後的資料。這樣,使用dp[j]形式,就能大量減少需要開闢陣列的空間。

規劃一點心得 學會放棄

最近,在做公司明年整體的研發規劃和產品規劃。但無論如何規劃,總覺得需要更多人力來參與 而今年11月中旬開規劃的專項會議時,已明確提出明年的人才招聘控制在較小的規模,公司通過提高管理和改進流程來提高內功,不進行較大規模產品線和團隊的擴張。開始時我認為,是不是產品線規劃的過於豐富了,但仔細審查發現這些工...

SQL一點心得

sql語句將所有 stock 表裡的 縮寫 led甲 替換改寫成 led刷 update dbo stock set 縮寫 replace 縮寫 led甲 led刷 where 縮寫 like led甲 go字首 update mytable set myfield replace myfield,...

Cell myCell一點心得

ctor initializer形如 cell cell mvalue 0 mstring ctor initializer,能在建立資料成員的同時賦初值 1.const方法的工作原理是將方法內用到的資料成員都標記為const引用。因此試圖修改資料成員時,編譯器報錯。2.用explicit關鍵字標記...