代码随想录训练营day44| 518. 零钱兑换 II 377. 组合总和 Ⅳ

简介: 代码随想录训练营day44| 518. 零钱兑换 II 377. 组合总和 Ⅳ

前言

代码随想录算法训练营day44


一、Leetcode 518. 零钱兑换 II

1.题目

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

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5] 输出:4 解释:有四种方式可以凑成总金额: 5=5 5=2+2+1 5=2+1+1+1 5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2] 输出:0 解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

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

提示:

1. 1 <= coins.length <= 300
2. 1 <= coins[i] <= 5000
3. coins 中的所有值 互不相同
4. 0 <= amount <= 5000

来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/coin-change-ii

2.解题思路

方法一:动态规划

这道题中,给定总金额 amountamount 和数组 coinscoins,要求计算金额之和等于 amountamount 的硬币组合数。其中,coinscoins 的每个元素可以选取多次,且不考虑选取元素的顺序,因此这道题需要计算的是选取硬币的组合数。

可以通过动态规划的方法计算可能的组合数。用 dp[x]dp[x] 表示金额之和等于 xx 的硬币组合数,目标是求 dp[amount]dp[amount]。

动态规划的边界是 dp[0]=1dp[0]=1。只有当不选取任何硬币时,金额之和才为 00,因此只有 11 种硬币组合。

对于面额为 coincoin 的硬币,当 coin≤i≤amountcoin≤i≤amount 时,如果存在一种硬币组合的金额之和等于 i−coini−coin,则在该硬币组合中增加一个面额为 coincoin 的硬币,即可得到一种金额之和等于 ii 的硬币组合。因此需要遍历 coinscoins,对于其中的每一种面额的硬币,更新数组 dpdp 中的每个大于或等于该面额的元素的值。

由此可以得到动态规划的做法:

1. 初始化 dp[0]=1dp[0]=1;
2. 
3. 遍历 coinscoins,对于其中的每个元素 coincoin,进行如下操作:
4.     遍历 ii 从 coincoin 到 amountamount,将 dp[i−coin]dp[i−coin] 的值加到 dp[i]dp[i]。
5. 
6. 最终得到 dp[amount]dp[amount] 的值即为答案。

上述做法不会重复计算不同的排列。因为外层循环是遍历数组 coinscoins 的值,内层循环是遍历不同的金额之和,在计算 dp[i]dp[i] 的值时,可以确保金额之和等于 ii 的硬币面额的顺序,由于顺序确定,因此不会重复计算不同的排列。

例如,coins=[1,2]coins=[1,2],对于 dp[3]dp[3] 的计算,一定是先遍历硬币面额 11 后遍历硬币面额 22,只会出现以下 22 种组合:

3=1+1+13=1+233=1+1+1=1+2

硬币面额 22 不可能出现在硬币面额 11 之前,即不会重复计算 3=2+13=2+1 的情况。

3.代码实现

```java class Solution { public int change(int amount, int[] coins) { int[] dp = new int[amount + 1]; dp[0] = 1; for (int coin : coins) { for (int i = coin; i <= amount; i++) { dp[i] += dp[i - coin]; } } return dp[amount]; } }
```

二、Leetcode 377. 组合总和 Ⅳ

1.题目

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

输入:nums = [1,2,3], target = 4 输出:7 解释: 所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1) 请注意,顺序不同的序列被视作不同的组合。

示例 2:

输入:nums = [9], target = 3 输出:0

提示:

1. 1 <= nums.length <= 200
2. 1 <= nums[i] <= 1000
3. nums 中的所有元素 互不相同
4. 1 <= target <= 1000

来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/combination-sum-iv

2.解题思路

方法一:动态规划

这道题中,给定数组 numsnums 和目标值 targettarget,要求计算从 numsnums 中选取若干个元素,使得它们的和等于 targettarget 的方案数。其中,numsnums 的每个元素可以选取多次,且需要考虑选取元素的顺序。由于需要考虑选取元素的顺序,因此这道题需要计算的是选取元素的排列数。

可以通过动态规划的方法计算可能的方案数。用 dp[x]dp[x] 表示选取的元素之和等于 xx 的方案数,目标是求 dp[target]dp[target]。

动态规划的边界是 dp[0]=1dp[0]=1。只有当不选取任何元素时,元素之和才为 00,因此只有 11 种方案。

当 1≤i≤target1≤i≤target 时,如果存在一种排列,其中的元素之和等于 ii,则该排列的最后一个元素一定是数组 numsnums 中的一个元素。假设该排列的最后一个元素是 numnum,则一定有 num≤inum≤i,对于元素之和等于 i−numi−num 的每一种排列,在最后添加 numnum 之后即可得到一个元素之和等于 ii 的排列,因此在计算 dp[i]dp[i] 时,应该计算所有的 dp[i−num]dp[i−num] 之和。

由此可以得到动态规划的做法:

1. 初始化 dp[0]=1dp[0]=1;
2. 
3. 遍历 ii 从 11 到 targettarget,对于每个 ii,进行如下操作:
4.     遍历数组 numsnums 中的每个元素 numnum,当 num≤inum≤i 时,将 dp[i−num]dp[i−num] 的值加到 dp[i]dp[i]。
5. 
6. 最终得到 dp[target]dp[target] 的值即为答案。

上述做法是否考虑到选取元素的顺序?答案是肯定的。因为外层循环是遍历从 11 到 targettarget 的值,内层循环是遍历数组 numsnums 的值,在计算 dp[i]dp[i] 的值时,numsnums 中的每个小于等于 ii 的元素都可能作为元素之和等于 ii 的排列的最后一个元素。例如,11 和 33 都在数组 numsnums 中,计算 dp[4]dp[4] 的时候,排列的最后一个元素可以是 11 也可以是 33,因此 dp[1]dp[1] 和 dp[3]dp[3] 都会被考虑到,即不同的顺序都会被考虑到。

3.代码实现

```java class Solution { public int combinationSum4(int[] nums, int target) { int[] dp = new int[target + 1]; dp[0] = 1; for (int i = 1; i <= target; i++) { for (int num : nums) { if (num <= i) { dp[i] += dp[i - num]; } } } return dp[target]; } }
```


相关文章
|
8月前
|
JavaScript 前端开发 Java
网上积分兑换商城的设计与实现(论文+源码)_kaic
网上积分兑换商城的设计与实现(论文+源码)_kaic
|
8月前
|
测试技术
代码随想录 Day37 完全背包理论基础 卡码网T52 LeetCode T518 零钱兑换II T377 组合总和IV
代码随想录 Day37 完全背包理论基础 卡码网T52 LeetCode T518 零钱兑换II T377 组合总和IV
69 0
|
算法
代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ
代码随想录算法训练营第四十三天 | LeetCode 518. 零钱兑换 II、377. 组合总和 Ⅳ
67 1
|
算法
代码随想录算法训练营第四十五天 | LeetCode 70. 爬楼梯、322. 零钱兑换、279. 完全平方数
代码随想录算法训练营第四十五天 | LeetCode 70. 爬楼梯、322. 零钱兑换、279. 完全平方数
88 1
算法练习Day44|70. 爬楼梯 (进阶)● 322. 零钱兑换 ● 279.完全平方数
算法练习Day44|70. 爬楼梯 (进阶)● 322. 零钱兑换 ● 279.完全平方数
|
机器学习/深度学习 算法
代码随想录训练营day45| 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数
代码随想录训练营day45| 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数
102 0
|
算法 前端开发
前端算法-零钱兑换
前端算法-零钱兑换
|
C++
C/C++每日一练(20230502) 卖树苗、数字归类、组合总和II
C/C++每日一练(20230502) 卖树苗、数字归类、组合总和II
143 0
|
存储 算法
LeetCode:322. 零钱兑换——动态规划从案例入门
题目描述:给你一个整数数组coins,表示不同面额的硬币;以及一个整数 amount,表示总金额。计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回-1。你可以认为每种硬币的数量是无限的。
173 0
代码随想录刷题|LeetCode 70. 爬楼梯(进阶) 322. 零钱兑换 279.完全平方数 139.单词拆分
代码随想录刷题|LeetCode 70. 爬楼梯(进阶) 322. 零钱兑换 279.完全平方数 139.单词拆分
代码随想录刷题|LeetCode 70. 爬楼梯(进阶) 322. 零钱兑换 279.完全平方数 139.单词拆分