動態規劃

做了一些動態規劃的題目, 總結了一下思路和分析過程.在此分享

一.分析方法:

動態規劃的關鍵在於如何把一個問題分解成子問題, 分解完問題就可以得出關係式, 得出關係式就可以編碼了. 以下是我分解問題的思路:

1.確定分析緯度:

​ 只有一個參考因素的話只需要一維數組記錄數據, 比如最長遞增序列, 只有自己一個序列在比較;

​ 比如最長子序列,涉及到兩個序列, 因爲其下標的變化不是同步的, 所以需要用二維數組記錄所有可能性.

2.確定數組代表含義:

​ 數組Dp[n]可以代表最終含義,比如例子中的爬樓梯問題, Dp[n]表示上到n階一共有多少種方式;

​ 有時候直接定義爲最終含義不利於分析問題, Dp[n]可以代表當前值的狀態,比如最長上升序列中,Dp[n]表示以n元素爲最右邊界的上升序列的長度;最大回文串中, Dp[i, j]無法記錄長度, 記錄的是迴文狀態.

3.分解成子問題:

​ 從Dp(n)出發分析與Dp(n-1)的聯繫, Dp(n)如何達到Dp(n-1)的狀態,自上而下,比如爬樓梯;

​ 從Dp(n-1)出發分析與Dp(n)的聯繫,Dp(n-1)如何達到Dp(n)的狀態,自下而上,例如最長連續子序列;

​ 也可能是Dp(n)與Dp(n-i)和Dp(i),把Dp(n)分解成Dp(n-i)和Dp(i),例如剪繩子問題

4.確定邊界

​ 整個過程是從無到有的過程, 起始點就是邊界值. 有些邊界值比較特殊, 需要一開始就明確

二.例子整理:

1.爬樓梯問題

描述:一次可以上一階,一次也可以上兩階,問:上到n階臺階,有幾種方式?

分析:

​ (1).找關係:

​ n階臺階可以由n-1階上一階到達;也可以由n-2階上2階到達.

​ (2).確定表達式:

​ Dp[n] = Dp[n-1] + Dp[n-2]

​ (3).確定邊界和初始值:

​ Dp[0] = 0; Dp[1] = 1; Dp[2] = 2;

2.硬幣找零問題

描述:有1,3,5元面額的硬幣, 買n元的商品,求買到商品需要支付最少的硬幣數?

分析:

​ (1).找關係:

​ n元的商品, 可以由n-1元的硬幣組合+1個1元面額買到;可以由n-3元的硬幣組合+1個3元面額買到;可以由n-5元的硬幣組合+1個5元面額買到. 因爲題目要求用最少的硬幣數,所以需要比較一下,然後獲取最少硬幣數的組合

​ (2).確定表達式:

​ Dp[n] = Min(Dp[n-1] + 1 ,Dp[n-3] + 1,Dp[n-5] +1)

​ (3).確定邊界和初始值:

​ Dp[0] = 0; Dp[1] = 1; Dp[2] = 2;Dp[3] = 1;Dp[4] = 2;Dp[5] = 1;

3.最小平方和問題

描述:給一個正整數 n, 找到若干個完全平方數(比如1, 4, 9, … )使得他們的和等於 n。你需要讓平方數的個數最少

分析:

​ (1).找關係:

​ n, 等於n - 1 + 1,平方數爲n-1的平方數+1; 等於n - 4 + 4, 平方數爲n-4的平方數+1; …

​ 判斷獲取最小平方個數

​ (2).確定表達式:

​ Dp[n] = Min(Dp[n-1] + 1 ,Dp[n-4] + 1,Dp[n-9] +1, …)

​ (3).確定邊界和初始值:

​ Dp[0] = 0; Dp[1] = 1; Dp[2] = 2;Dp[3] = 3;Dp[4] = 4;…

4.最長遞增序列

描述:設L=<a1,a2,…,an>是n個不同的實數的序列,L的遞增子序列是這樣一個子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值

分析:

​ (1).找關係:

​ 首先思考n與n-1之間的關係, LIS[n]表示長度爲n的序列的最大遞增序列長度, LIS[n-1]表示長度爲n-1的序列最大遞增序列長度. 假如L[n]的值大於LIS[n-1]中最大遞增序列中的最大值,那LIS[n] = LIS[n-1] + 1.否則可能會產生多條長度一樣的序列, n增加的時候, 要判斷多條序列的最大值, 不方便維護.
另外一種關係是, 記錄以數組每個元素爲最大值得遞增序列的長度記爲Dp[n]. n與n-1的關係即爲:遍歷0 - n-1假如L[n]的值大於L[j], 並且Dp[j] + 1 大於 Dp[i], 那麼Dp[i]的長度 = Dp[j]+ 1,最後選出Dp數組最大的即爲m.

​ (2).確定表達式:

​ (3).確定邊界和初始值:

5.最長公共子序列

描述:給定兩個序列X和Y,稱序列Z是X和Y的公共子序列如果Z既是X的一個子序列,又是Y的一個子序列,求最大長度

分析:

​ (1).找關係:​

​ 子序列不連續:

​ 二維數組關係. 假設獲取到最長序列時, X處在i位置, Y處在j位置.Dp(i, j)即爲最大長度.

​ 假如X[i] == Y[j], 那麼Dp[i, j] = Dp[i - 1, j -1] + 1; 否則Dp[i, j] = Max( Dp[i - 1, j], Dp[i, j - 1] )

​ 子序列連續:

​ 二維數組關係.以Dp[i, j]代表以X[i], Y[j]作爲子序列成員的子序列的長度.

​ 假如 X[i] == Y[j], 那麼Dp[i, j] = Dp[i - 1, j -1] + 1; 否則Dp[i, j] = 0

​ 相比Dp(i, j)作爲兩個長度爲i, j的序列的最長子序列長度的含義來說, 這樣的定義更方便程序記錄長度值. 並且最後只需要比較一下, 就可以得出最大值.

​ (2).確定表達式:

​ 子序列不連續:

​ Dp[i, j] =0; i == 0 || j == 0

​ Dp[i, j] = Dp[i - 1, j -1] + 1; X[i] == Y[j]

​ Dp[i, j] = Max( Dp[i - 1, j], Dp[i, j - 1] ) X[i] != Y[j]

​ 子序列連續:

​ Dp[i, j] =0; i == 0 || j == 0

​ Dp[i, j] = Dp[i - 1, j -1] + 1; X[i] == Y[j]

​ Dp[i, j] = 0; X[i] != Y[j]

​ (3).確定邊界和初始值:

​ Dp[0, j] = 0; Dp[i, 0] = 0;

6.減繩子問題

描述:給一根長度爲N的繩子,請把繩子剪成M段(m,n都是整數),每段繩子的 長度記爲k[0],k[1],k[2]…. 獲取k[0],k[1],k[2] 的最大乘積

分析:

​ (1).找關係:​

​ 這裏是一維數組的關係,設Dp[n]爲長度爲n的繩子剪出來的最大乘積. Dp[n]和Dp[n-1]的關係不容易找, 那麼就來找n與n-i的關係. 長度爲n的乘積等於長度爲n-i的乘積乘上長度爲i的繩子的乘積. 這個值不一定是最大的,所以最後需要輪詢遍歷出最大值.

​ (2).確定表達式:

​ Dp[n] = Max(Dp[n-1]*Dp[i]);

​ (3).確定邊界和初始值:

​ Dp[n] = n;

7.編輯距離問題

描述:給定兩個單詞 word1 和 word2,計算出將 word1 轉換成 word2 所使用的最少操作數 。

可以對單詞進行的操作有三種:插入一個字符;刪除一個字符;替換一個字符

例:

輸入: word1 = "horse", word2 = "ros"輸出: 3
horse -> rorse (將 'h' 替換爲 'r')
rorse -> rose (刪除 'r')
rose -> ros (刪除 'e')

分析:

​ (1).找關係:​

​ 定義Dp[i, j]爲最小操作數, i爲word1的長度, j爲word2的長度.

​ 假如W1[i] == W2[j], 那麼就下降到Dp[i - 1, j -1];

​ 否則就需要對W1進行操作:替換即爲Dp[i - 1, j -1] + 1; 插入即爲Dp[i , j - 1] + 1, 理解爲單詞1長度爲i, 單詞2長度爲j-1, 操作次數爲Dp[i , j - 1], 這時候單詞2的長度+1, 那麼單詞1相應地也應該插入一個字符; 刪除即爲Dp[i - 1, j] + 1

​ (2).確定表達式:

​ Dp[i, j] = Dp[i - 1, j - 1]; W1[i] == W2[j]

​ Dp[i, j] = Min( Dp[i - 1, j - 1], Dp[i - 1, j], Dp[i, j - 1] ) + 1; W1[i] != W2[j]

​ (3).確定邊界和初始值:

​ Dp[0, j] = j; Dp[x, 0] = x;

8.揹包問題

描述:有N件物品和一個容量爲V的揹包。第i件物品的容量是c[i],價值是v[i]。將物品裝入揹包可使這些物品的費用總和不超過揹包容量,求解價值總和最大值

分析:

​ (1).找關係:​

​ 變量是物品數量i和容量v, 設Dp(i, j)爲在容量j中放入i件物品中的某幾件所獲得的最大價值.Dp之間的聯繫取決於第i件物品裝與不裝, 每件物品都有裝與不裝兩種可能性, 也正是這種機制保證了這種解法的全面性.

​ 假如第i件物品不可以裝入(c[i] > j), 那麼Dp[i, j]的值就等於Dp[i - 1, j]

​ 假如第i件物品可以裝入(c[i] < j),並且裝入, 那麼Dp[i, j] 等於 v[i] + Dp[i, j - c[i]]

​ 假如第i件物品可以裝入(c[i] < j),但不裝入, Dp[i, j]的值就等於Dp[i - 1, j], 這裏需要獲取裝入和不裝入的較大值

​ (2).確定表達式:

​ Dp[i, j] = Dp[i - 1, j]; c[i] > j

​ Dp[i, j] = Max( Dp[i - 1, j], v[i] + Dp[i, j - c[i]] ); c[i] < j

​ (3).確定邊界和初始值:​

9.最長迴文串

描述:求字符串str的最大回文串

分析:

​ (1).找關係:​

​ 迴文串的子串也是迴文串, 依據這個就可以分解成子問題. 需要獲取的是長度信息, 這裏的Dp無法像最大遞增序列一樣記錄長度, 所以只能記錄狀態. 設Dp[i, j]表示從位置i開始導位置j的字符串是迴文串, 值記爲true. 那麼Dp[i + 1, j - 1]也是迴文串, 假如str[i - 1] == str[j + 1], 那麼Dp[i - 1, j + 1]也是迴文串. 根據此原則, 以起始位置和迴文長隊作爲下標, 通過兩層嵌套循環判斷出長度爲3 的迴文串, 長度爲3的, 長度爲4的…, 最後比較獲取最大的長度的

​ (2).確定表達式:

​ (3).確定邊界和初始值:​

​ Dp[i, i] = true;

​ 初始化Dp[i, i+1]的值

發佈了49 篇原創文章 · 獲贊 11 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章