當時大學開的那麼多演算法課為啥一節都不好好聽講!
動態規劃,是一種解決棘手問題的方法,它將問題分成小問題,並從解決小問題作為起點,從而解決最終問題的一種方法。
看不明白沒關係,後面我們會從幾個例項中逐漸讓大家摸清規律。
假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個台階。你有多少種不同的方法可以爬到樓頂呢?
注意:給定 n 是乙個正整數。
輸入: 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。
1 階 + 1 階
2 階
輸入: 3
輸出: 3
解釋: 有三種方法可以爬到樓頂。
1 階 + 1 階 + 1 階
1 階 + 2 階
2 階 + 1 階
你可能會這麼想
走1階台階只有一種走法,但是走2階台階有兩種走法(如示例1),如果n是雙數,我們可以湊成m個2級台階,每個m都有兩種走法,如果n是單數,那麼我們可以湊成m個2級台階加上乙個1級台階,這樣就似乎於乙個排列組合題目了,但是開銷貌似比較大。
如何將整個問題化成乙個乙個的小問題
這個時候使用動態規劃就很有用,因為這個問題其實是由乙個很簡單的小問題組成的。 觀察這種小問題,簡單地我們可以採用首位或者中間態進行一次分析,比如我們從最終態進行分析:
走n階台階,最後一步必定是1步或者2步到達。
那麼n階台階的走法不就相當於最後走一步和最後走兩步的走法的總和嗎?換一種方式來說,我們取乙個中間態:如果總共有3級台階,3級台階的走法只會存在兩種大的可能:走了1階台階+走兩步、走了兩級台階+走一步,即3級台階的所有走法就是走了1階台階的走法加上走了2階台階的走法,而1階台階的走法只有一種,2階台階的走法有2種,所有3階台階的走法有3種,我們使用一種更通用的方式進行表達的話就是所謂的狀態轉換方程:
有了這個公式,我們就可以使用迭代來完成整個過程,尋求到最終的ways[n]的值了,迭代的開始即我們已知的確定條件:一階台階只有一種走法:ways[1]=1、兩階台階有兩種走法:ways[2]=2,**如下:
實現**
public int climbstairs(int n) else if(n==2)
//避免使用0,即下標從1開始,更好理解
int ways=new int[n+1];
//賦值迭代初始條件
ways[1]=1;
ways[2]=2;
//利用狀態轉換方式進行迭代
for(int i=3;i<=n;i++)
return ways[n];
}複製**
基本流程
從上面的解決途徑我們可以發現基本流程是這樣的:
乙個機械人位於乙個 m x n 網格的左上角 (起始點在下圖中標記為「start」 )。
機械人每次只能向下或者向右移動一步。機械人試圖達到網格的右下角(在下圖中標記為「finish」)。
問總共有多少條不同的路徑?
例如,上圖是乙個7 x 3 的網格。有多少可能的路徑?
說明:m 和 n 的值均不超過 100。
輸入: m = 3, n = 2
輸出: 3
解釋:從左上角開始,總共有 3 條路徑可以到達右下角。
向右 -> 向右 -> 向下
向右 -> 向下 -> 向右
向下 -> 向右 -> 向右
輸入: m = 7, n = 3
輸出: 28
解決方法
相信沿用問題一的套路很多人已經知道該怎麼辦了,從乙個二維陣列的左上(0,0)走到右下(m,n)有多少種走法,且只能往右和往下走,那麼如果要走到(m,n),那麼我們的上一步只能是(m-1,n)或者(m,n-1),所以走到(m,n)的所有走法就是走到(m-1,n)的所有走法+走到(m,n-1)的所有走法,即可以得到狀態轉換方程:
但是,這個問題還有一些其他的問題限制需要我們考慮到,即走到兩側的時候,只會有乙個方向的走法,(上方只會有ways[m-1][n]乙個方式,左側只會有ways[m][n-1]乙個方式)即下圖:
我們需要對這兩種方式進行限制,在這裡我在外圍再擴充套件了一圈,將整個方格擴充套件為**(m+1)*(n+1)**的方格,來避開限制,當然也可以直接限制(後續會講到),但是將其所有的值都設定為0,即相當於設定了限制。
實現**
public static int uniquepaths(int m, int n)
//邊上擴充套件一列,使其值為0
for(int j=0;j<=m;j++)
//設定初始值,起點走法為1,只能一步一步走
ways[1][1]=1;
for(int a=1;a<=m;a++)
//套用狀態轉換方程
ways[a][b]=ways[a][b-1]+ways[a-1][b];
} }return ways[m][n];
}複製**
給定乙個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和為最小。
說明:每次只能向下或者向右移動一步。
輸入:[[1,3,1],
[1,5,1],
[4,2,1]
]輸出: 7
解釋: 因為路徑 1→3→1→1→1 的總和最小。
解決方法
這個問題與問題二及其相似,但是其涉及到乙個最優解的問題,現在每乙個點都有乙個類似權重的值,我們要使這個值最小,其實用問題二的想法,我們很快就能得到答案:走到(m,n)只能從(m-1,n)和(m,n-1)兩個地方走過來,那麼要保證(m,n)的權重最小,那麼我們只需要選擇走到(m-1,n)和(m,n-1)權重較小的那一邊即可,那麼我們就可以得到新的狀態轉移方程:
即走到當前點的權重=走到前一步權重的較小值+當前點的權重,並且該問題也有針對邊上元素的特殊處理
**
public static int minpathsum(int grid) {
//權重儲存陣列
int sum=new int[grid.length][grid[0].length];
//起點初始權重確定值
sum[0][0]=grid[0][0];
for(int i=0;i複製**
給定乙個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。
[[2],
[3,4],
[6,5,7],
[4,1,8,3]
]自頂向下的最小路徑和為 11(即,2 + 3 + 5 + 1 = 11)。
解決方法
這個問題可以理解為問題三的變種,但是他沒有乙個固定的終點,因為我們之前的方法都是從最後一步開始分析的,所以很多人也就對該問題無從下手了。但是其實我們也可以將最後一行的任何乙個元素作為終點,因為該問題起點確定,並且終點必定在最後一行。但是為了代表性,我們還是選取1或8為例子,如果最終達到1,需要上一排達到6或5。如果要達到5,那麼需要上一排達到3或4,所以我們由此可以得到該問題的狀態轉移方程:
這樣我們就可以根據問題三的模式找到達到最後一排所有可能終點(4,1,8,3)的最小權重,我們再從所有權重中選取最小值即可,該問題也有針對邊上元素的特殊處理。
實現**
public static int minimumtotal(list> ********) {
//建立狀態儲存陣列
int sum=new int[********.size()][********.size()];
//起點確定,權重確定
sum[0][0]=********.get(0).get(0);
for(int i=0;i複製**
(參考《演算法**》) 動態規劃基本思想
動態規劃與貪心策略類似,將乙個問題的解決方案視為一系列決策的結果。不同的是,貪心演算法每採用一次貪心選擇便做出乙個不可撤回的決策,而在動態規劃中,還要考察每個最優決策序列中是否包含乙個最優決策自序列。使用動態規劃時,所求問題應具有以下兩種性質。1.最優子結構性質 所求問題的最優子結構性質是採用動態規...
演算法基本思想之動態規劃
動態規劃 談到演算法最優解 則首先會想到兩個演算法第乙個是貪心演算法,其次才會是動態規劃 再者從問題解決規模的相似程度來看,可能會引出另外的乙個演算法叫做 分治法 更準確的來講叫做分治思想 有乙個問題是 現在假定學校要從計算機工程系選出本年度的三好學生,假定其名額一共有三個,其中學校計算機工程系的學...
動態規劃演算法的基本思想 演算法 動態規劃
演算法 動態規劃 首先學習動態規劃,我們要先知道什麼是動態規劃?演算法導論這本書是這樣介紹這個演算法的,動態規劃與分治方法類似,都是通過組合子問題的解來來求解原問題的。再來了解一下什麼是分治方法,以及這兩者之間的差別,分治方法將問題劃分為互不相交的子問題,遞迴的求解子問題,再將它們的解組合起來,求出...