From 8dc3a668839cb7df2ffe181b4251952a45abe3b7 Mon Sep 17 00:00:00 2001 From: Sainnhe Park Date: Thu, 9 Feb 2023 17:06:22 +0800 Subject: [PATCH] s0518 --- include/s0518_coin_change_ii.hpp | 13 +++++++++++++ notes/src/knapsack.md | 23 +++++++++++++++++++---- src/s0518_coin_change_ii.cpp | 13 +++++++++++++ tests/s0518_coin_change_ii.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 include/s0518_coin_change_ii.hpp create mode 100644 src/s0518_coin_change_ii.cpp create mode 100644 tests/s0518_coin_change_ii.cpp diff --git a/include/s0518_coin_change_ii.hpp b/include/s0518_coin_change_ii.hpp new file mode 100644 index 0000000..3fed452 --- /dev/null +++ b/include/s0518_coin_change_ii.hpp @@ -0,0 +1,13 @@ +#ifndef S0518_COIN_CHANGE_II_HPP +#define S0518_COIN_CHANGE_II_HPP + +#include + +using namespace std; + +class S0518 { + public: + int change(int amount, vector& coins); +}; + +#endif diff --git a/notes/src/knapsack.md b/notes/src/knapsack.md index 65a7da2..3ffcf02 100644 --- a/notes/src/knapsack.md +++ b/notes/src/knapsack.md @@ -100,6 +100,15 @@ void knapsack_problem_1d() { **A:** 如果从后往前的话,`dp[j - weight[i]] + value[i]` 就用的是上一层的数据(这才是我们想要的),但如果从前往后的话,`dp[j - weight[i]] + value[i]` 就用的是这一层的数据,这将会导致物品被重复放进去。 +**Q: 怎样初始化?** + +**A:** + +1. 先确定 `dp[j]` 应该初始化为多少,一般是 `0` +2. 接下来确定 `dp[0]` 应该初始化为多少,我们直接看下一次访问到 `dp[0]` 时是什么情况就行,当访问到 `dp[0]` 时它应该是多少 +3. 我们接下来看看用当前的初始化值跑 `i = 0` 也就是第一层,逻辑是否正确。如果逻辑正确,那么第一层 for 循环的 `i` 就从 `0` 开始 +4. 如果不正确,我们专门对 `i = 0` 也就是第一层进行初始化,然后第一层 for 循环的 `i` 从 `1` 开始。 + ### [416. 分割等和子集](https://leetcode.cn/problems/partition-equal-subset-sum/) 二维数组: @@ -129,8 +138,6 @@ void knapsack_problem_1d() { 本题中可以不初始化第一层,然后 `i` 从 0 开始。 -考虑需不需要初始化第一层其实很简单,就看如果不初始化的话,第一层循环的逻辑是否正确。 - ### [1049. 最后一块石头的重量 II](https://leetcode.cn/problems/last-stone-weight-ii/) 仔细思考一下每个石头重量的加减方式,你会发现其实最终的重量可以这样表示: @@ -164,8 +171,6 @@ void knapsack_problem_1d() { 本题中可以不初始化第一层,然后 `i` 从 0 开始。 -考虑需不需要初始化第一层其实很简单,就看如果不初始化的话,第一层循环的逻辑是否正确。 - ## 完全背包 和 01 背包的区别就在于,01 背包的每个元素只能用一次,而完全背包的每个物品能够重复使用。 @@ -186,3 +191,13 @@ void complete_knapsack_problem_1d() { } } ``` + +### [518. 零钱兑换 II](https://leetcode.cn/problems/coin-change-ii/) + +- `dp[j]` 的含义是从 0 ~ i 这些硬币中选择,组合成总金额为 j 的组合数 +- `dp[j] += dp[j - coins[i]]` +- 初始化: + - 当 `i = 0` 的时候,也就是只有 `coins[0]` 这一种硬币的时候,当 `j` 可以被 `coins[0]` 整除的时候就赋值为 `1`,否则赋值为 `0` + - 一种特殊情况是 `dp[0]`,它表示组合成总金额为 `0` 的组合数,我们必须把它赋值为 `1`,这是因为当我们执行 `dp[j] += dp[j - coins[i]]` 的时候,如果 `j == coins[i]` 那么显然应该自增 `1`。 + - 看看能不能不初始化第一层,我们直接删掉代码试试发现可以,那就不初始化第一层。 +- 遍历顺序都是从前向后 diff --git a/src/s0518_coin_change_ii.cpp b/src/s0518_coin_change_ii.cpp new file mode 100644 index 0000000..2f945cb --- /dev/null +++ b/src/s0518_coin_change_ii.cpp @@ -0,0 +1,13 @@ +#include "s0518_coin_change_ii.hpp" + +int S0518::change(int amount, vector& coins) { + int len = coins.size(); + vector dp(amount + 1, 0); + dp[0] = 1; + for (int i{0}; i < len; ++i) { + for (int j = coins[i]; j <= amount; ++j) { + dp[j] += dp[j - coins[i]]; + } + } + return dp[amount]; +} diff --git a/tests/s0518_coin_change_ii.cpp b/tests/s0518_coin_change_ii.cpp new file mode 100644 index 0000000..f0c0749 --- /dev/null +++ b/tests/s0518_coin_change_ii.cpp @@ -0,0 +1,27 @@ +#include "s0518_coin_change_ii.hpp" + +#include + +TEST(Problem518, Case1) { + vector coins{1, 2, 5}; + int amount{5}; + int expected{4}; + S0518 solution; + EXPECT_EQ(solution.change(amount, coins), expected); +} + +TEST(Problem518, Case2) { + vector coins{2}; + int amount{3}; + int expected{0}; + S0518 solution; + EXPECT_EQ(solution.change(amount, coins), expected); +} + +TEST(Problem518, Case3) { + vector coins{10}; + int amount{10}; + int expected{1}; + S0518 solution; + EXPECT_EQ(solution.change(amount, coins), expected); +}