1.定义
动态规划是分治思想的延伸,也就是大事化小,小事化了的意思
动态规划问题一般从这四个方面考虑:
1.状态定义:根据问题把子问题抽象出来
2.状态间的转移方程定义:状态与状态之间的递推关系
3.状态的初始化
4.返回结果
适用场景:最大值、最小值,可行不可行,是不是,方案个数
2.第一题Fibonacci 斐波那契数列
状态F(i):第 i 项的值
状态转移方程:F(i) = F(i - 1) + F(i - 2)
初始状态:F(0) = 0,F(1) = 1
返回结果:F(n)
代码表示:
public class Solution {
// 时间复杂度O(n)
public int Fibonacci(int n) {
int[] arr = new int[n + 1];
arr[0] = 0;
arr[1] = 1;
for(int i = 2;i <= n;i++){
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
}
}
这是一个典型的动态规划,每一步都把之前的状态保存了下来,然后进行计算。
优化:可以优化一下空间复杂度,不用再前面定义了一个n + 1长度的数组
public class Solution {
public int Fibonacci(int n) {
if(n == 0){
return 0;
}
if(n == 1){
return 1;
}
int sum = 0;
int n1 = 1;
int n2 = 0;
for(int i = 2;i <= n;i++){
sum = n1 + n2;
// 更新中间的状态
n2 = n1;
n1 = sum;
}
return sum;
}
}
2.字符串分割
139. 单词拆分https://leetcode-cn.com/problems/word-break/
状态F(i):字符串前 i 个字符是否可以被分割
输入: s = "leetcode", wordDict = ["leet", "code"] 输出: trueF(4):前四个字符是否可以被分割:true
F(8):F(4) && 【5,8】是否可以在词典中找到:true
F(0) && 【1,8】是否可以在词典中找到
F(1) && 【2,8】是否可以在词典中找到
F(2) && 【3,8】是否可以在词典中找到
...
F(7) && 【8,8】是否可以在词典中找到
状态转移方程:F(i):j < i && F(j) && 【j + 1,i】是否可以在词典中找到
初始状态:F(0):true
返回结果:F(字符串长度):f(s.length())
代码表示:
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
boolean[] canBreak = new boolean[s.length() + 1];
// 初始化
canBreak[0] = true;
for(int i = 1;i <= s.length();i++){
// j < i && F(j) && [j + 1,i]
for(int j = 0;j < i;j++){
if(canBreak[j] && wordDict.contains(s.substring(j,i))){
canBreak[i] = true;
break;
}
}
}
return canBreak[s.length()];
}
}
3.三角矩阵
public int minTrace (int[][] triangle) {
int len = triangle.length;
if (len == 0) return 0;
// f(i,j)=Min{f(i+1,j), f(i+1,j+1)} + val(i, j)
int[][] array = new int[len][len];
for (int i = len-1; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
if (i == len-1) {
array[i][j] = triangle[i][j];
} else {
array[i][j] = Math.min(array[i+1][j], array[i+1][j+1]) + triangle[i][j];
}
}
}
return array[0][0];
}