零钱兑换
核心思路
-
就是完全背包的写法, 只是这个题是求最小值, 所以初始化的时候不能初始化0, 而是Integer.max_value
-
以及赋值的时候, 如果dp[j - coin] = max, 就跳过, 说明这个硬币无法兑换=j
javapublic static int coinChange(int[] coins, int amount) { //dp[i] = 下标为i的时候 能填满的最少硬币数 //推导 //如果不放coins[i], dp[j]不变 //如果放了coins[i], dp[j] = dp[j - coins[i]] + 1 //初始化 int[] dp = new int[amount + 1]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for (int coin : coins) { for (int j = coin; j <= amount; j++) { if(dp[j - coin] == Integer.MAX_VALUE){ continue; } dp[j] = Math.min(dp[j], dp[j - coin] + 1); } } return dp[amount] == Integer.MAX_VALUE ? 0 : dp[amount]; }
完全平方数
核心思路
-
和零钱兑换思路几乎一致
-
dp定义: dp[j]代表的是 和为j的完全平方数的最小数量
-
只是n即是物品, 也是背包容量
javapublic static int numSquares(int n) { int[] dp = new int[n + 1]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for (int i = 1; i <= n; i++) { long sqrt = (long) Math.sqrt(i); //不是完全平方数就跳过 if (sqrt * sqrt != (long) i) continue; for (int j = i; j <= n; j++) { if (dp[j - i] == Integer.MAX_VALUE) continue; dp[j] = Math.min(dp[j], dp[j - i] + 1); } } return dp[n]; } -
算法优化后
- 外层i直接平方, 这样就不用判断i是否是完全平方数, i * i 一定是从小到大的完全平方数, 1、4、9等
javaint[] dp = new int[n + 1]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0; for (int i = 1; i * i <= n; i++) { for (int j = i * i; j <= n; j++) { if (dp[j - i * i] == Integer.MAX_VALUE) continue; dp[j] = Math.min(dp[j], dp[j - i * i] + 1); } } return dp[n];
单词拆分
核心思路
- 还是完全背包问题
- 但是该题 不是求组合,而是排列
- 因为单词是要求有序的,比如 leetcode,[code,leet], 就要先leet, 再code
例如
-
输入: s = "catsanddog", wordDict = ["cat", "cats", "and", "sand", "dog"]
-
i = 3: j=0 匹配 "cat",dp[3] = T。
-
i = 4: j=0 匹配 "cats",dp[4] = T。
-
i = 7 ("catsand"):
-
尝试 j = 3: dp[3] 是 T (cat),剩下 s[3:7] 是 "sand"。字典有 "sand",所以 dp[7] = T。
-
尝试 j = 4: dp[4] 是 T (cats),剩下 s[4:7] 是 "and"。字典也有 "and",所以 dp[7] 再次被确认是 T。
(只要有一个 j 能通,dp[7] 就是 true) -
i = 10: j=7 匹配 "dog",且 dp[7] 是 T,所以 dp[10] = T。
-
结果: 成功。
javapublic static boolean wordBreak(String s, List<String> wordDict) { boolean[] dp = new boolean[s.length() + 1]; //dp的定义: 下标为i, 代表的是 从 0...i-1的单词能够被wordDict拼接而来 dp[0] = true; for (int i = 1; i <= s.length(); i++) { for (int j = 0; j < i; j++) { dp[i] = dp[j] && wordDict.contains(s.substring(j, i)); if (dp[i]) break; } } return dp[s.length()]; }