所謂動態規劃就是用之前問題的答案來推到當前問題的答案。
解動態規劃問題的步驟:
1.問題拆解,尋找規律與聯繫
2.狀態定義
3.推導遞推方程
4.具體實現(注意邊界條件與特殊情況)
下面看具體實例:
青蛙跳臺階問題
一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先後次序不同算不同的結果)。
來看這個動態規劃問題的解法:
1.問題拆解,尋找規律與聯繫
跳到n階的最後一步,要麼是從n-1跳上來,要麼是從n-2跳上來。
也就是說跳到n階的結果,可以由跳到n-1階的結果和跳到n-2階的結果相加即可。
2.狀態定義
dp[i] 定義爲從起點到達第i階的跳法總數
。
3.推導遞推方程
dp[i] = dp[i - 1] + dp[i - 2]
4.具體實現(注意邊界條件與特殊情況)
注意邊界條件是
dp[0] = 0 ;dp[1] = 1 ; dp[2] = 2;
因此很容易得到此題答案代碼:
class Solution {
public:
int jumpFloor(int number) {
//動態規劃方法:
// 跳到n階的最後一步,要麼是從n-1跳上來,要麼是從n-2跳上來
// p[n] = p[n-1]+p[n-2];(n>=3)
if (number <= 0) {
return -1;
} else if (number == 1) {
return 1;
} else if (number ==2) {
return 2;
}
int pre = 2;
int prepre = 1;
int result;
for(int i = 3 ; i <= number ; i++){ //注意從第三階開始
result = pre + prepre;
prepre = pre;
pre = result;
}
return result;
}
};
連續子數組的最大和
給一個數組,返回它的最大連續子序列的和。例如:{6,-3,-2,7,-15,1,2,2},連續子序列的最大和爲8(從第0個開始,到第3個爲止)。
1.問題拆解,尋找規律與聯繫
很自然考慮以i結尾的最大連續子數組的和
與以i-1結尾的最大連續子數組的和
有什麼聯繫?
當以i-1結尾的最大連續子數組的和
小於零時,以i結尾的最大連續子數組的和
就是第i個元素。
當以i-1結尾的最大連續子數組的和
不小於零時,以i結尾的最大連續子數組的和
就是第i個元素與以i-1結尾的最大連續子數組的和
之和。
2.狀態定義
dp[i] 定義爲以i結尾的最大連續子數組的和
。
3.推導遞推方程
dp[i] = max{ dp[i-1] + array[i], array[i] }.
4.具體實現(注意邊界條件與特殊情況)
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> a) {
if(!a.size()) return 0;
int res= 0x80000000;
for(int i = 0, s = 0; i < a.size(); ++i){
s = max(s + a[i], a[i]);
res= max(res, s);
}
return res;
}
};
三角形最小路徑和
轉載自:五分鐘學算法
LeetCode 第 120 號問題:三角形最小路徑和。
題目描述
給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。
例如,給定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自頂向下的最小路徑和爲 11(即,2 + 3 + 5 + 1 = 11)。
問題拆解:
這裏的總問題是求出最小的路徑和,路徑是這裏的分析重點,路徑是由一個個元素組成的,和之前爬樓梯那道題目類似,[i][j] 位置的元素,經過這個元素的路徑肯定也會經過 [i - 1][j] 或者 [i - 1][j - 1],因此經過一個元素的路徑和可以通過這個元素上面的一個或者兩個元素的路徑和得到。
狀態定義
狀態的定義一般會和問題需要求解的答案聯繫在一起,這裏其實有兩種方式,一種是考慮路徑從上到下,另外一種是考慮路徑從下到上,因爲元素的值是不變的,所以路徑的方向不同也不會影響最後求得的路徑和,如果是從上到下,你會發現,在考慮下面元素的時候,起始元素的路徑只會從[i - 1][j] 獲得,每行當中的最後一個元素的路徑只會從 [i - 1][j - 1] 獲得,中間二者都可,這樣不太好實現,因此這裏考慮從下到上的方式
,狀態的定義就變成了 “最後一行元素到當前元素的最小路徑和
”,對於 [0][0] 這個元素來說,最後狀態表示的就是我們的最終答案。
遞推方程
“狀態定義” 中我們已經定義好了狀態,遞推方程就出來了
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]
實現
這裏初始化時,我們需要將最後一行的元素填入狀態數組中,然後就是按照前面分析的策略,從下到上計算即可
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[][] dp = new int[n][n];
List<Integer> lastRow = triangle.get(n - 1);
for (int i = 0; i < n; ++i) {
dp[n - 1][i] = lastRow.get(i);
}
for (int i = n - 2; i >= 0; --i) {
List<Integer> row = triangle.get(i);
for (int j = 0; j < i + 1; ++j) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + row.get(j);
}
}
return dp[0][0];
}