所用代码 java
爬楼梯 LeetCode 70
题目链接:爬楼梯 LeetCode 70 - 简单
思路
本题使用完全背包的思路求解
- dp[j] :爬到楼层为j的地方,需要dp[j]种方法
- 递推公式:dp[j] += dp[j-nums[i]]
- 初始化:dp[0] = 1 , 以免dp[j]累加为0
- 遍历顺序:先走1步再走2步,和先走2步再走1步是一样的,所以是排列问题,应先遍历背包,再遍历物品
- 打印
class Solution {
public int climbStairs(int n) {
int[] dp = new int[n+1];
int[] nums = {1,2}; // 物品
dp[0] = 1;
for (int j = 1; j <= n; j++){ // 背包
for (int i = 0; i < nums.length; i++) { // 物品
if (j>=nums[i]) dp[j] += dp[j-nums[i]];
}
}
return dp[n];
}
}
总结
本题楼梯只能爬1或2阶,若还能爬3、4、5...m,就更是一个完全背包的题的,完全可以从一个简单的dp转换为较为难的题,且注意的是:先走1再走2 与 先走2再走1 是不一样的,所以是一个排列问题,就得先遍历背包,再遍历物品。
零钱兑换 LeetCode 322
题目链接:零钱兑换 LeetCode 322 - 中等
思路
硬币无限,是一个完全背包问题,且和背包问题2类似
-
dp[j] : 在coins中无限次选硬币i,恰好凑成总金额j的硬币个数最少有dp[j]种
-
递推公式:dp[j] = min(dp[j], dp[j-coins[i]] + 1)
- dp[j] 表示没有加物品
- dp[j-coins[i]] + 1 表示添加了物品,即就要加1
-
初始化:
- dp[0] = 1
- 非零下标,初始为 Integer.maxValue,因为我们是最小值min,取0容易背
-
遍历顺序:求最少的元素数量,先遍历物品还是先遍历背包都可以
-
打印
class Solution {
public int coinChange(int[] coins, int amount) {
int maxValue = Integer.MAX_VALUE;
int[] dp = new int[amount+1];
Arrays.fill(dp, maxValue);
dp[0] = 0;
for (int i = 0; i < coins.length; i++) { // 物品
for (int j = coins[i]; j <= amount; j++) { // 背包
// 由于coins[i]最大值为2^31 - 1,再加1的话为:-2147483648
// 所以这里要避免整数会越界的情况
if (dp[j-coins[i]] != maxValue){
dp[j] = Math.min(dp[j], dp[j-coins[i]] + 1);
// System.out.printf("dp[%d]=%d ",j,dp[j]);
}
}
// System.out.println();
}
return dp[amount] == maxValue ? -1 : dp[amount];
}
}
总结
第一、本题求的是最小的元素数量,没有涉及到组合或排列的方案,所以对于遍历顺序无要求。
第二、递推公式dp[j] = Math.min(dp[j], dp[j-coins[i]] + 1) 由于我们凑j - coins[i]的最小个数为dp[j - coins[i]],所以再凑dp[j]的时候就相当于多了一个coins,就需要 + 1
第三、初始化为max是为了放在求min的时候,把前面的结果给覆盖掉,且后面dp[j-coins[i]]有执行+1的操作,需排除dp[j-coins[i]]=max的情况,否则就会超出max最大值变为负数。
完全平方数 LeetCode 279
题目链接:完全平方数 LeetCode 279 - 中等
思路
某个数i求平方之后加起来等于n,i可以多取,完全背包问题。
- dp[j]:和为j的完全平方数的最小个数为dp[j]
- 递推公式:dp[j] = min(dp[j-i*2] + 1, dp[j])
- 初始化:dp[0] = 0,由于取最小值,其他为max
- 遍历顺序:求的是最小个数,与顺序无关
- 打印
class Solution {
public int numSquares(int n) {
int[] dp = new int[n+1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
dp[1] = 1;
for (int i = 1; i < n; i++) { // 物品
for (int j = i*i; j <= n; j++) { // 背包
if (dp[j-i*i] != Integer.MAX_VALUE){
dp[j] = Math.min(dp[j], dp[j-i*i]+1);
}
// System.out.printf("dp[%d]=%d ",j, dp[j]);
}
// System.out.println();
}
return dp[n];
}
}
总结
这个上上一题有一个区别是就,返回的时候至少有1的平方能凑成完全平方数,
或者不用初始化两个数可以这样写:
第一个for循环遍历物品 for (int i = 1; i * i < n; i++),就可以把j=1的情况考虑到。