代码随想录刷题笔记 DAY 50 | 爬楼梯 进阶版 | 零钱兑换 No. 322 | 完全平方数 No.279

Day 50

01. 爬楼梯(进阶版)

题目链接

代码随想录题解

<1> 题目

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

输入描述

输入共一行,包含两个正整数,分别表示n, m

输出描述

输出一个整数,表示爬到楼顶的方法数。

输入示例

3 2

输出示例

3

提示信息

数据范围:1 <= m < n <= 32;

当 m = 2,n = 3 时,n = 3 这表示一共有三个台阶,m = 2 代表你每次可以爬一个台阶或者两个台阶。

此时你有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶段
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶
<2> 笔记

在刷本题之前建议大家将昨天的 零钱兑换II(No. 518)吃透,可以去看一下我的博客,里面对为什么 先遍历背包得到的是排列,先遍历物品得到的是组合 进行了详细的解释:

代码随想录刷题笔记 DAY 43 | 完全背包基础 | 零钱兑换 II No.518 | 组合总和 IV No.377

给定一个楼梯高度 n,我们每次可以从 [1, m] 中选择 一个步数去爬这个楼梯,问有多少种方法可以到达顶层。这其实就是一个完全背包问题的变式,也就是背包容量为 n 的一个背包,每次可以选择的物品的重量为 [1, m] 问有多少种方法将背包填满;这和昨天那道题目的递推公式差不多,即
dp[j] += dp[j - nums[i]] 但本题中是以 m 的形式给出的,所以其实 nums[i] = i

接下来就是去判断本题是 排列还是选择问题,即 1 2 11 1 2 是不是同一种方法,通过题目描述可以很容易看出来本题是排列问题,所以先去遍历背包得到如下的递推公式:

for (int j = 0; j <= n; j++) {
    for (int i = 1; i <= m; i++) {
    	if (j >= i) dp[j] = dp[j] + dp[j - i];
    }
}

由于本题和昨天的非常类似,这里并没有做很详细的描述,大家把昨天那道题搞懂之后会发现本题非常容易。

<3> 代码
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[] dp = new int[n + 1];
        dp[0] = 1;
        for (int j = 0; j <= n; j++) {
            for (int i = 1; i <= m; i++) {
                if (j >= i) dp[j] = dp[j] + dp[j - i];
            }
        }
       System.out.println(dp[n]);
    }
}

02. 零钱兑换(No. 322)

题目链接

代码随想录题解

<1> 题目

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104
<2> 笔记

本题也是一个类背包问题,给定一个背包容量,问装满这个背包 最少需要多少个物品;大家看到这类题目可能会感到有些熟悉,因为前面做到过装满这个背包 最多 可以使用多少个物品的题目:一和零(No. 474),这里附上我的题解,感兴趣的可以去看一下:代码随想录刷题笔记 DAY 42 | 最后一块石头的重量 II No.1049 | 目标和 No.494 | 一和零 No.474

对于一个硬币同样有两种选择:取或者不取,如果不取的话那结果就仍然是 dp[j] (由于本题是完全背包问题所以这里的取是指的某一个硬币),如果要取得话,是要建立在 dp[j - coins[i]] 的基础上来取的,即 dp[j - coins[i]] + 1,由于本题问的是最小需要多少种,所以就是对这两个值取一个最小值。

然后就是本题的初始化条件,这次就不能随便初始化为 0 了,因为如果 dp[i]0 的话,那无论经过几次取最小值得到的都是 0 ,所以这里就将其初始化成 Integer.MAX_VALUE 此时才能保证每次取最小是有效的,而将一个 int 赋值成为 Integer.MAX_VALUE 再看一下递归公式中的这一部分 dp[j - coins[i]] + 1 这部分会导致数组的越界,而同时如果一个节点是初始化的值,也恰好说明是无法通过这个节点的状态去推出其他节点的,所以到遇到值仍然是初始化的值的节点应该 直接跳过

        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j < amount + 1; j++) {
                if (dp[j - coins[i]] != max) {
                    dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
                }
            }
        }
<3> 代码
class Solution {
    public int coinChange(int[] coins, int amount) {
        int max = Integer.MAX_VALUE;
        int[] dp = new int[amount + 1];
        if (amount == 0) return 0;
        for (int j = 0; j < dp.length; j++) {
            // 初始化
            dp[j] = max;
        }
        dp[0] = 0;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j < amount + 1; j++) {
                if (dp[j - coins[i]] != max) {
                    dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
                }
            }
        }
        return dp[amount] ==max ? -1 : dp[amount];
    }
}

03. 完全平方数(No. 279)

题目链接

代码随想录题解

<1> 题目

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,14916 都是完全平方数,而 311 不是。

示例 1:

输入:n = 12
输出:3
解释:12 = 4 + 4 + 4

示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9

提示:

  • 1 <= n <= 104
<2> 笔记

本题是给定一个数字,来求得这个数 最少 能由几个完全平方数构成,而完全平方数指的是值等于一个 整数的平方 的数。

这里补充一下判断一个数是否是完全平方数的方法,因为本题的动规解法并不会用到这个方法

 public boolean isPerfectSquare(int x) {
        int y = (int) Math.sqrt(x);
        return y * y == x;
    }

刷过上一题的朋友会发现本题和上题非常类似,甚至还会比较简单,因为在 1 也是完全平方数,所以不会出现 前面的数字仍然是初始化的值的的情况

首先来确认 dp 数组的含义,dp[j]j 最少由 dp[j] 个完全平方数来构成,而 dp[j] 可以通过
dp[j - i * i] 来求得,其实这些数字就是物品,而数字的平方就是它的 value,从头开始遍历物品即可来逐次取最小值即可。

其实还有一种方式就是 dp[j] = Math.min(dp[j], dp[j - i] + dp[i]) 比如说 15 可以拆分成 114213 这样逐次求取直到遍历完求取最小值,但是要使用这种方式必须要先将 完全平方数 的部分全部初始化为 1 所以要比上面那种方式慢一些。

接下来就是初始化了,本题和上题相同都是要去求取最小值,所以为了有效的取值,要初始化为 Integer.MAX_VALUE

遍历顺序:由于本题不涉及排列组合问题,是一个类似于完全背包的题目,所以先遍历背包还是先遍历物品都可以。

然后就是 dp[0] 要初始化为 0 否则求解就没有意义了,全部都是初始化的值了。

<3> 代码
class Solution {
    public int numSquares(int n) {
        int[] dp = new int[n + 1];
        int max = Integer.MAX_VALUE;
        for (int i = 0; i < dp.length; i++) {
            dp[i] = max;
        }
        dp[0] = 0;
        for (int i = 1; i < dp.length; i++) {
            for (int j = 1; j * j <= i; j++) {
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }
        return dp[n];
    }
}
  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*Soo_Young*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值