【代码训练营】day45 | 70. 爬楼梯 (进阶)& 322. 零钱兑换 & 279.完全平方数

117 阅读3分钟

所用代码 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的情况考虑到。