老實說,我感覺動態規劃還是很難的,最常見的一種困境就是看答案自己能看明白,但自己想就是想不出來。
網上關於動態規劃的講解也有很多,我認為,學會動態規劃的關鍵,就是我們需要形成自己的解題思路,遇見動態規劃的題就要知道應該往哪方面去思考。
那怎麼才能形成自己的解題思路呢?無非就是通過做題+整理的方式。動態規劃是有一定的套路的,掌握的套路之後,可以說大部分題就可以做出來了。
這篇文章我先解釋一下動態規劃的解題步驟,然後重點講解幾個案例,來初步認識一下動態規劃演算法。
動態規劃解題的3個步驟,不同的人有不同的表述,但大體意思是相同的。如下:
定義dp陣列的含義:
dp陣列可能是一維陣列,也可能是二維陣列,用來儲存歷史記錄。定義dp陣列的含義,也就是定義dp[i]表示的是什麼。這個步驟不是很好找出,需要通過做題來掌握常見的套路。
找出陣列元素之間的關係式:
也就是說,dp[i]的值是可能由dp[i-1]、dp[i-2]...來推出來的。這一步驟是最關鍵的一步,通常也是不容易找出的。
找出初始值:
我們知道,只知道遞推關係,是不能求出來具體值的,所以我們必須要找出初始值才能求解。
看不懂?沒事,下面我通過4道例題來講解一下,並嚴格按照上面的四個步驟來做。
給定乙個整數陣列 nums ,找到乙個具有最大和的連續子陣列(子陣列最少包含乙個元素),返回其最大和。示例:輸入: [-2,1,-3,4,-1,2,1,-5,4]輸出: 6解釋:連續子陣列 [4,-1,2,1] 的和最大,為 6。本題是leetcode第53題,難度為 簡單
力扣leetcode-cn.com
解題思路:
定義dp陣列含義:dp[i]表示以nums[i]為結尾的子序和;
找出陣列元素的關係式:我們知道,以dp[i]結尾的子序可以分為兩種情況,一種是和nums[i]之前的元素連在一起構成的序列,一種是不和之前的元素連在一起,也就是單獨nums[i]乙個元素構成的序列。dp[i]應該是在兩者之間選擇乙個最大值。
dp[i]=math.max(dp[i-1]+nums[i],nums[i]);
3.找出初始條件:當i=0時,就沒辦法再用上面的公式進行計算。即
dp[0]=nums[0].
當陣列為空時,返回0;
當陣列只含有1個元素時,返回這個元素
最後,返回的結果應該是dp陣列中元素的最大值。
【**】(有詳細註解)
class solution
return max;
} }
案例二 打家劫舍問題本題是leetcode 第198題,難度為 簡單
力扣leetcode-cn.com
你是乙個專業的小偷,計畫偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。解題思路還是動態規劃解題的3個步驟:給定乙個代表每個房屋存放金額的非負整數陣列,計算你 不觸動警報裝置的情況下 ,一夜之內能夠偷竊到的最高金額。示例1:輸入:[1,2,3,1]輸出:4解釋:偷竊1號房屋(金額=1),然後偷竊3號房屋(金額=3),偷竊到的最高金額=1+3=4.示例2:輸入:[2,7,9,3,1]輸出:12解釋:偷竊 1 號房屋(2) , 偷竊 3 號房屋 ( 9),接著偷竊 5 號房屋 (1)。
偷竊到的最高金額 = 2 + 9 + 1 = 12
明確dp陣列含義:dp[i]表示前i個房屋能偷盜的最高金額(存在第0個房間)
2. 找出遞推關係:對於第i個房屋,只有兩種選擇,選和不選。
若是選第i個房屋,則第i-1個房屋不能選,dp[i]=nums[i]+dp[i-2];
若是不選,則dp[i]=dp[i-1].
dp[i]應是在兩者之中選擇大的那個。
dp[i]=math.max(nums[i]+dp[i-2],dp[i-1]);
3. 明確邊界條件:
nums.length=0時,返回0;
nums.length=1時,返回nums[0];
nums.length=2時,返回math.max(nums[0],nums[1]);
最後結果返回dp[m-1].
**:
class solution
if(nums.length==1)
//找出nums陣列中的最大值
int max=nums[0];
for(int i=0;i案例四 最小路徑和
給定乙個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和為最小。
說明:每次只能向下或者向右移動一步。示例:輸入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]輸出:7解釋: 因為路徑 1→3→1→1→1 的總和最小。
本題是leetcode第64題,難度為 中等
力扣leetcode-cn.com
解題思路
同樣,應用動態規劃解題三步驟,只不過這次dp陣列是二維陣列:
1. 明確dp陣列的含義:dp[i][j]表示從左上角走到(i,j)這個位置時最小的路徑和,這樣dp[m-1][n-1]就是所要求的。
2. 寫出遞推關係式:因為每次只能向下走或者向右走,所以有兩種方式到達,一種是從(i-1,j)向下走一步,一種是(i,j-1)向右走一步到達,選擇其中一種路徑較小的,再加上(i,j)位置的值即可
dp[i][j]=math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
3. 找出初始值:當i或者j為0時,就不能用上面的公式計算,所以初始值就是dp[0][0...n-1]和dp[0...m-1][0];
也很容易計算得出:
dp[0][j]=dp[0][j-1]+grid[0][j];
dp[i][0]=dp[i-1][0]+grid[i][0];
**:
class solution {
public int minpathsum(int grid) {
int m=grid.length;
int n=grid[0].length;
int dp=new int [m][n];
//邊界條件
dp[0][0]=grid[0][0];
for(int i=1;i小結
上面這些題,算是動態規劃的一些入門題,但對於初學者來說還是有一定難度的。之後會再寫一些關於動態規劃的題目,還有一些如何優化的,希望對大家有幫助啊。
動態規劃 什麼是動態規劃?
先來看看 資訊學奧賽一本通第5版 是怎麼說的 動態規劃程式設計是對解最優化問題的一種途徑 一種方法,而不是一種特殊演算法。不像前面所述的那些搜尋或數值計算那樣,具有乙個標準的數學表示式和明確清晰的解題方法。動態規劃程式設計往往是針對一種最優化問題,由於各種問題的性質不同,確定最優解的條件也互不相同,...
什麼是動態規劃
動態規劃 dynamic programming 與分治法相似,都是通過組合子問題的解來求解原問題,動態規劃應用於子問題重疊的情況,即不同的子問題具有公共的子子問題。通常按照下面4個步驟來設計乙個動態規劃演算法 1.刻畫乙個最優解的結構特徵。2.遞迴地定義最優解的值 3.計算最優解的值,通常採用自底...
什麼是動態規劃?
一 基本思想 態規劃演算法的基本思想與分治法類似,都是將問題大問題拆分為小問題,通過小問題的求解來得到最後的解。與分治法不同的是,分治法是分而治之,分治法將大問題拆分為相同性質的子問題,最後合併子問題的解來構成最終解。而動態規劃是,將子問題拆解後,按順序求解子問題,前面階段的求解為後一階段提供有用資...