動態規劃Dynamic Programming:通過把原問題分解爲相對簡單的子問題的方法來求解複雜問題的方法。
把求解的問題分成多個階段或多個子問題,然後按照順序求解各子問題。前一子問題的解,爲後一問題的求解提供了有用信息。依次解決各個子問題,最後一個階段的解就是初始問題的解。
動態規劃問題,大致可以通過以下4個步驟:
1)劃分狀態,即劃分子問題
2)狀態表示,即如何讓計算機理解子問題。
3)狀態轉移,即父問題是如何由一個子問題或者多個子問題得到的。
4)確定邊界,確定初始狀態是什麼?最小的子問題?最終狀態又是什麼。
Leetcode 120 Triangle
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[ [2], [3,4], [6,5,7], [4,1,8,3] ]
The minimum path sum from top to bottom is 11
(i.e., 2 + 3 + 5 + 1 = 11).
1)子問題 將原問題分解成求解從以每個座標結尾的最小path sum
2)狀態表示 dp[i][j]表示以第i行第j列結尾的最小path sum的值
3)狀態轉移 dp[i][j] = min{dp[i-1][j]+triangle[i][j], dp[i-1][j-1] + triangle[i][j]}
4)最小子問題dp[0] ,最終狀態 dp[triangle.length-1]數組的最小值
/*
dp[i][j] = min{dp[i-1][j]+triangle[i][j], dp[i-1][j-1] + triangle[i][j]}
return min{dp[n][j]} 0<=j<n
*/
public int minimumTotal(List<List<Integer>> triangle) {
List<Integer> upRowList, curRowList;
for(int i = 1; i < triangle.size(); ++i) {
upRowList = triangle.get(i-1);
curRowList = triangle.get(i);
for (int j = 0; j < curRowList.size(); ++j){
if(j -1 >= 0 && j < upRowList.size()){
curRowList.set(j,Math.min(upRowList.get(j-1) + curRowList.get(j), upRowList.get(j) + curRowList.get(j)));
}else if(j-1 >= 0){
curRowList.set(j, upRowList.get(j-1) + curRowList.get(j));
}else{
curRowList.set(j, upRowList.get(j) + curRowList.get(j));
}
}
}
List<Integer> lastRowList = triangle.get(triangle.size()-1);
lastRowList.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
return lastRowList.get(0);
}
Leetcode 139 Word Break
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Input: s = "applepenapple", wordDict = ["apple", "pen"] Output: true Explanation: Return true because"
applepenapple"
can be segmented as"
apple pen apple"
.
1)子問題 將原問題分解成從0開始的字串
2)狀態表示 dp[i]表示從0開始的長度爲i的字串sub能否滿足word break
2)狀態轉移 dp[i] = dp[i-word_0.length] || ... || dp[i-word_m.length] , word_x出現在子串的末尾
3)最小子問題dp[0] , 最終狀態 dp[s.length],
// dynamic programming
// dp[n] = dp[n-word[0].length] || dp[n-word[1].length] || ....|| dp[n-word[k].length]
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null || s.length() == 0)
return true;
boolean[] dp = new boolean[s.length()+1];
dp[0] = true;
for(int i = 1; i <= s.length(); ++i){
dp[i] = false;
String sub = s.substring(0,i);
for(int k = 0; k < wordDict.size(); ++k){
String word = wordDict.get(k);
if(sub.contains(word) && sub.lastIndexOf(word) == sub.length()-word.length())
dp[i] = dp[i] || dp[i-word.length()];
}
}
return dp[s.length()];
}