爬楼梯问题
class Solution { public int climbStairs(int n) { if(n == 1 || n == 2) { return n; } int first = 1, second = 2; for(int i = 3; i <= n; i++) { int t = second; second = first + second; first = t; } return second; }}
class Solution { public int coinChange(int[] coins, int amount) { // 定义状态: // dp[i][j] 表示第 i 个面额的硬币决策完后,金额能达到 j 的最少硬币个数 // 状态转移方程为:dp[i][j] = min(dp[i][j - (0 ~ k) * coins]) int n = coins.length; int[][] dp = new int[n][amount + 1]; // 初始化标识符 for(int i = 0; i < n; i++){ Arrays.fill(dp[i], Integer.MAX_VALUE); } // 初始化第一层 for(int i = 0; i <= amount / coins[0]; i++) { dp[0][i * coins[0]] = i; } for(int i = 1; i < n; i++) { for(int j = 0; j <= amount; j++) { int k = j / coins[i]; for(int c = 0; c <= k; c++) { if(j >= c * coins[i] && dp[i - 1][j - c * coins[i]] != Integer.MAX_VALUE) { dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - c * coins[i]] + c); } } } } return dp[n - 1][amount] == Integer.MAX_VALUE ? -1 : dp[n - 1][amount]; }}
class Solution { public int coinChange(int[] coins, int amount) { // 定义状态: // dp[i][j] 表示第 i 个面额的硬币决策完后,金额能达到 j 的最少硬币个数 // 状态转移方程为:dp[i][j] = min(dp[i][j - (0 ~ k) * coins]) int n = coins.length; int[][] dp = new int[n][amount + 1]; // 初始化标识符 for(int i = 0; i < n; i++){ Arrays.fill(dp[i], Integer.MAX_VALUE); } // 初始化第一层 for(int i = 0; i <= amount / coins[0]; i++) { dp[0][i * coins[0]] = i; } for(int i = 1; i < n; i++) { for(int j = 0; j <= amount; j++) { int k = j / coins[i]; for(int c = 0; c <= k; c++) { if(j >= c * coins[i] && dp[i - 1][j - c * coins[i]] != Integer.MAX_VALUE) { dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - c * coins[i]] + c); } } } } return dp[n - 1][amount] == Integer.MAX_VALUE ? -1 : dp[n - 1][amount]; }}
class Solution { public int cuttingRope(int n) { if(n < 2) { return 1; } if(n == 2 || n == 3) { return n - 1; } int[] dp = new int[n + 1]; dp[1] = 1; dp[2] = 2; dp[3] = 3; for(int i = 4; i <= n; i++) { int max = Integer.MIN_VALUE; int low = (i & 1) == 1 ? i / 2 + 1 : i / 2; for(int j = i - 1; j >= low; j--) { max = Math.max(max, dp[j] * dp[i - j]); } dp[i] = max; } return dp[n]; }}
class Solution { public int translateNum(int num) { // 定义状态:dp[i] 表示当前 i 长度的数字存在多少种解法 // dp[i] 状态只能从上1个数字或上两个数字演化而来,前提是两数字之和不能超过25 // 状态转移方程: // 当两数字之和超过25 dp[i] = dp[i - 1] // 当两数字之和不能超过25 dp[i] = dp[i - 1] + dp[i - 2] List<Integer> list = new ArrayList<>(); while(num != 0) { list.add(num % 10); num /= 10; } int size = list.size(); int[] array = new int[size]; for(int i = 0; i < size; i++) { array[i] = list.get(size - 1 - i); } int[] dp = new int[size + 1]; dp[0] = 1; for(int i = 1; i <= size; i++) { dp[i] += dp[i - 1]; if(i >=2 && valid(array[i - 2], array[i - 1])) { dp[i] += dp[i - 2]; } } return dp[size]; } private boolean valid(int a, int b) { if(a == 1) { return true; } if(a == 2 && b <= 5) { return true; } return false; }}
class Solution { public boolean wordBreak(String s, List<String> wordDict) { // 定义状态: // dp[i] 表示当前 i 长度的字符串能否被空格拆分为一个或多个在字典 wordDict 中出现的单词 // 假设字典 wordDict 中word1、word2、word3能跟 s[0~i - 1]的后缀匹配起来,那么说明到达 i 这个状态只能从 i - len1,i-len2,i-len3 这三个状态转移过来 // 状态转移方程:dp[i] = dp[i - len1] || dp[i - len2] || dp[i - len3] int l = s.length(); boolean[] dp = new boolean[l + 1]; dp[0] = true; for(int i = 0; i <= l; i++) { for(String word : wordDict) { int len = word.length(); int start = i -len; if(start >= 0 && s.startsWith(word, start) && dp[i - len]) { dp[i] = true; break; } } } return dp[l]; }}
匹配问题
class Solution { public int longestCommonSubsequence(String text1, String text2) { // 定义状态:dp[i][j] 表示长度为 i 的字符串 s1 和 长度为 j 的字符串 s2 之间的最长公共子序列 // 当 text1[i] == text2[j] 时,相当于两字符串都多了一个相同的字符,则 dp[i][j] = dp[i - 1][j - 1] + 1 // 当 text1[i] != text2[j] 时,那么 dp[i][j] 的状态只能从 dp[i - 1][j] 或者 dp[i][j - 1] 处到达,那么 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); int m = text1.length(), n = text2.length(); int[][] dp = new int[m + 1][n + 1]; for(int i = 1; i <= m; i++) { char c = text1.charAt(i - 1); for(int j = 1; j <= n; j++) { if(c == text2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[m][n]; }}
class Solution { public int minDistance(String word1, String word2) { // 定义状态:dp[i][j] 表示长度为 i 的 word1 转换成 长度为 j 的 word2 所使用的最少操作数 // 当 word1[i] = word2[j],dp[i][j] = dp[i - 1][j - 1] // 当word1[i] != word2[j]: // 如果是替换字符:dp[i - 1][j - 1] // 如果是插入一个字符: dp[i][j] = dp[i][j - 1] + 1; // 如果是删除一个字符 dp[i][j] = dp[i - 1][j] + 1; // dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1 int m = word1.length(), n = word2.length(); if(m * n == 0) { return m + n; } int[][] dp = new int[m + 1][n + 1]; for(int i = 1; i <= m; i++) { dp[i][0] = i; } for(int i = 1; i <= n; i++) { dp[0][i] = i; } for(int i = 1; i <= m; i++) { for(int j = 1; j <= n; j++) { if(word1.charAt(i - 1) == word2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; } else { dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1; } } } return dp[m][n]; }}
其他
class Solution { private int count = 0; public int pathSum(TreeNode root, int targetSum) { // 树形DP,状态从左右子树转移而来,利用DFS来做 // 用 Map 来存储以每个节点作为root节点的路径情况,key为路径和,value为路径条数 dfs(root, targetSum); return count; } private Map<Integer, Integer> dfs(TreeNode node, int targetSum) { if(node == null) { return new HashMap<Integer, Integer>(); } Map<Integer, Integer> nodeMap = new HashMap(); nodeMap.put(node.val, 1); Map<Integer, Integer> leftNodeMap = dfs(node.left, targetSum); Map<Integer, Integer> rightNodeMap = dfs(node.right, targetSum); // 遍历左子节点的Map for(Map.Entry<Integer, Integer> entry : leftNodeMap.entrySet()) { Integer newKey = entry.getKey() + node.val; Integer newValue = entry.getValue(); if(nodeMap.containsKey(newKey)) { newValue += nodeMap.get(newKey); } nodeMap.put(newKey, newValue); } // 遍历右子节点的Map for(Map.Entry<Integer, Integer> entry : rightNodeMap.entrySet()) { Integer newKey = entry.getKey() + node.val; Integer newValue = entry.getValue(); if(nodeMap.containsKey(newKey)) { newValue += nodeMap.get(newKey); } nodeMap.put(newKey, newValue); } // 遍历当前节点的 Map for(Map.Entry<Integer, Integer> entry : nodeMap.entrySet()){ if(entry.getKey() == targetSum) { count += entry.getValue(); } } return nodeMap; }}
class Solution { public int lengthOfLIS(int[] nums) { // 定义状态: // dp[i] 表示以 nums[i] 整数结尾的最长严格递增子序列的长度 // 当nums[i] > nums[j]时,dp[i] = max(dp[j]) + 1 (j 的取值范围为 0 ~ j) // 状态转移方程式为: dp[i] = max(dp[j]) + 1; nums[i] > nums[j] && j < i // 那么整个数组中,最长严格递增的子序列的长度为:max(dp[0] ~ dp[i]) int n = nums.length; int[] dp = new int[n]; dp[0] = 1; int max = Integer.MIN_VALUE; for(int i = 1; i < n; i++) { for(int j = i - 1; j >= 0; j--) { if(nums[i] > nums[j]) { dp[i] = Math.max(dp[i], dp[j]); } } dp[i]++; max = Math.max(max, dp[i]); } return Math.max(max, dp[0]); }}