状态转移方程是动态规划问题首先要找到的
174. Dungeon Game
思路:初始化一个dp数组,大小是整型的最大值,代表当前位置所需的最小血量
- 首先初始化公主位置的右方、下方的值为1
- 从公主所在位置开始,与右方、下方相比,找到当前位置所需的最小血量(正数:当前位置所需血量为1;负数:右方、下方最小血量与当前相减即可得到)

1 // time:o(m*n) 2 // space:o(m*n) 3 class Solution { 4 public int calculateMinimumHP(int[][] dungeon) { 5 int len = dungeon[0].length; 6 int dep = dungeon.length; 7 int[][] dp = new int[dep + 1][len + 1]; 8 for (int i = 0; i <= dep; i++) { 9 for (int j = 0; j <= len; j++) { 10 dp[i][j] = Integer.MAX_VALUE; 11 } 12 } 13 dp[dep][len - 1] = 1; 14 dp[dep - 1][len] = 1; 15 for (int i = dep - 1; i >= 0; i--) { 16 for (int j = len - 1; j >= 0; j--) { 17 // dp数组增加是为了方便编程,这样可以在遍历数组的时候直接使用i+1,j+1 18 dp[i][j] = Math.max(1, Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]); 19 } 20 } 21 return dp[0][0]; 22 } 23 }
注意:
- 每次搜索当前位置最小血量时,要取最大值
303. Range Sum Query - Immutable
思路:使用一维数组记录记录原数组开始位置到当前位置的和

1 // time: o(n) + m * o(1) = o (m + n) m次查询 2 // space: o(n) 3 class NumArray { 4 5 private int[] dp; 6 7 public NumArray(int[] nums) { 8 dp = new int[nums.length + 1]; 9 for (int i = 0; i < nums.length; i++) { 10 dp[i + 1] = dp[i] + nums[i]; 11 } 12 } 13 14 public int sumRange(int i, int j) { 15 return dp[j + 1] - dp[i]; 16 } 17 } 18 19 /** 20 * Your NumArray object will be instantiated and called as such: 21 * NumArray obj = new NumArray(nums); 22 * int param_1 = obj.sumRange(i,j); 23 */
312. Burst Balloons
c[i][j]表示位置i与位置j之间最大的分数,将该问题分解为小问题
- 选出最后一个要爆的气球位置k,此时可以将问题分解为c[i][k-1] + nums[i -1] * nums[k] * nums[j + 1] + c[k + 1][j]
- 三层循环讲解如下:
- 小问题:c[i][j]长度最小1的开始,直到长度最大n-2
- i的选取,从1开始,可以一直取值到n-len - 1;j的值一定为left+len-1
- 开始循环,找到此时从i到j的最大值,此时小问题已经求出答案

1 // time: o(n ^ 3) 2 // space: o(n ^ 2) 3 class Solution { 4 public int maxCoins(int[] nums) { 5 int[] mid = new int[nums.length + 2]; 6 int n = 1; 7 for (int num : nums) { 8 if (num > 0) { 9 mid[n++] = num; 10 } 11 } 12 mid[0] = mid[n++] = 1; 13 14 int[][] dp = new int[n][n]; 15 for (int len = 1; len <= n - 2; len++) { 16 for (int left = 1; left <= n - len - 1; left++) { 17 int right = left + len - 1; 18 for (int i = left; i <= right; i++) { 19 dp[left][right] = Math.max(dp[left][right], dp[left][i - 1] + 20 mid[left - 1] * mid[i] * mid[right + 1] + dp[i + 1][right]); 21 } 22 } 23 } 24 return dp[1][n - 2]; 25 } 26 }
注意:
- 可以将原数组的0值去除
以下是另外一种解法

1 class Solution { 2 public int maxCoins(int[] nums) { 3 int[] mid = new int[nums.length + 2]; 4 int n = 1; 5 for (int num : nums) { 6 if (num > 0) { 7 mid[n++] = num; 8 } 9 } 10 mid[0] = mid[n++] = 1; 11 int[][] mem = new int[n][n]; 12 return burst(mem, mid, 1, n - 2); 13 } 14 private int burst(int[][] mem, int[] nums, int left, int right) { 15 if (mem[left][right] > 0) { 16 return mem[left][right]; 17 } 18 int res = 0; 19 for (int i = left; i <= right; i++) { 20 res = Math.max(res, burst(mem, nums, left, i - 1) + 21 nums[left - 1] * nums[i] * nums[right + 1] + burst(mem, nums, i + 1, right)); 22 } 23 mem[left][right] = res; 24 return res; 25 } 26 }
70. Climbing Stairs
思路:比较简单

1 // time: o(n) 2 class Solution { 3 public int climbStairs(int n) { 4 int[] res = new int[3]; 5 if (n <= 1) { 6 return 1; 7 } 8 res[0] = res[1] = 1; 9 for (int i = 2; i <= n; i++) { 10 res[i % 3] = res[(i - 1) % 3] + res[(i - 2) % 3]; 11 } 12 return res[n % 3]; 13 } 14 }
113. Path Sum II
思路:DFS

1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public List> pathSum(TreeNode root, int sum) { 12 List
> res = new ArrayList<>(); 13 List
path = new ArrayList<>(); 14 findSum(res, path, root, sum); 15 return res; 16 } 17 private void findSum(List > res, List
path, TreeNode root, int sum) { 18 if (root == null) { 19 return; 20 } 21 sum = sum - root.val; 22 if (root.left == null && root.right == null) { 23 if (sum == 0) { 24 path.add(root.val); 25 res.add(new ArrayList (path)); 26 // DFS一定要移除元素 27 path.remove(path.size() - 1); 28 } 29 return; 30 } 31 path.add(root.val); 32 findSum(res, path, root.left, sum); 33 findSum(res, path, root.right, sum); 34 path.remove(path.size() - 1); 35 } 36 }
120. Triangle
思路:动规

1 class Solution { 2 public int minimumTotal(List> triangle) { 3 int n = triangle.size(); 4 int[][] dp = new int[n][n]; 5 // dp[i][j] = minTotalOf(i, j) 表达式含义 6 // 1、状态转移方程:dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1]) + triangle[i][j] 7 for (int i = 0; i < n; i++) { 8 for (int j = 0; j <= i; j++) { 9 dp[i][j] = triangle.get(i).get(j); 10 // 2、边界条件 11 if (i == 0 && j == 0) { 12 continue; 13 } else if (j == 0) { 14 dp[i][j] += dp[i - 1][j]; 15 } else if (j == i) { 16 dp[i][j] += dp[i - 1][j - 1]; 17 } else { 18 dp[i][j] += Math.min(dp[i - 1][j], dp[i - 1][j - 1]); 19 } 20 } 21 } 22 int res = Integer.MAX_VALUE; 23 for (int i = 0; i < n; i++) { 24 res = Math.min(res, dp[n - 1][i]); 25 } 26 return res; 27 } 28 }
方法二:自下而上的动规,节省空间space: o(n)

1 class Solution { 2 public int minimumTotal(List> triangle) { 3 int n = triangle.size(); 4 int[] dp = new int[n]; 5 for (int i = 0; i < n; i++) { 6 dp[i] = triangle.get(n - 1).get(i); 7 } 8 for (int k = n - 2; k >= 0; k--) { 9 for(int i = 0; i <= k; i++) { 10 dp[i] = Math.min(dp[i], dp[i + 1]) + triangle.get(k).get(i); 11 } 12 } 13 return dp[0]; 14 } 15 }