动态规划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
3)状态转移 dp[i] = dp[i-word_0.length] || ... || dp[i-word_m.length] , word_x出现在子串的末尾
4)最小子问题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()];
}
474. Ones and Zeroes
In the computer world, use restricted resource you have to generate maximum benefit is what we always want to pursue.
For now, suppose you are a dominator of m 0s and n 1s respectively. On the other hand, there is an array with strings consisting of only 0s and 1s.
Now your task is to find the maximum number of strings that you can form with given m 0s and n 1s. Each 0 and 1 can be used at most once.
Note:
- The given numbers of
0sand1swill both not exceed100 - The size of given string array won't exceed
600
Example:
Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3
Output: 4
Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are “10,”0001”,”1”,”0”
1)子问题 将原问题分解成数组中包含k个字符串,1的资源为i个,0的资源为j个的问题
2)状态表示 dp[k][i][j]表示字符串数组包含中前k个字符串,拥有i个1, j个0的时候获取的字串的最大数量
3)状态转移 每次增加一个字符串时,dp[k][i][j] = Math.max(1 + dp[k - 1][i - count[0]][j - count[1]], dp[k - 1][i][j]); for each i,j
4) 最小子问题dp[0][i][j],表示字符串数组为空 , 最终状态 dp[str.length][m][n],
实现代码:
public int findMaxForm(String[] strs, int m, int n) {
int[][][] dp = new int[strs.length+1][m+1][n+1];
for (int k = 1; k<= strs.length; ++k) {
int[] count = count(strs[k-1]);
for (int i=0;i <=m;i++)
for (int j=0;j<=n;j++) {
dp[k][i][j] = Math.max(dp[k-1][i][j], dp[k][i][j]);
if(i >= count[0] && j >= count[1])
dp[k][i][j] = Math.max(1 + dp[k - 1][i - count[0]][j - count[1]], dp[k - 1][i][j]);
}
}
return dp[strs.length][m][n];
}
public int[] count(String str) {
int[] res = new int[2];
for (int i=0;i<str.length();i++)
res[str.charAt(i) - '0']++;
return res;
}Since each i and j will not be influenced by higher i,j, and dp[k-1] will not be used in the future, we can iterate backward and update dp[i][j] in place.
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m+1][n+1];
for (String s : strs) {
int[] count = count(s);
for (int i=m;i>=count[0];i--)
for (int j=n;j>=count[1];j--)
dp[i][j] = Math.max(1 + dp[i-count[0]][j-count[1]], dp[i][j]);
}
return dp[m][n];
}
public int[] count(String str) {
int[] res = new int[2];
for (int i=0;i<str.length();i++)
res[str.charAt(i) - '0']++;
return res;
}