-
Notifications
You must be signed in to change notification settings - Fork 0
322. Coin Change #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
322. Coin Change #38
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # 322. Coin Change | ||
|
|
||
| そのまま思いついた方法として、メモ化再帰を実装: sol1.py | ||
| - 一発で通ったので気持ち良い | ||
|
|
||
| 再帰関数を使わずに書く | ||
|
|
||
| - 動的計画法: | ||
|
|
||
| - 最短経路と考えればBFSで書ける | ||
| - tableを保持したsol3.pyを書いたが、coinをソートすれば早期breakができること、levelがそのまま答えになることに気づき、改良: sol4.py | ||
|
|
||
| ## 計算量 | ||
| amount = O(n), len(coins) = O(m) | ||
| ### sol1.py | ||
| - 時間計算量 O(nm), 空間計算量 O(n) (=スタックフレーム、キャッシュ) | ||
| ### sol2.py | ||
| - 時間計算量 O(nm), 空間計算量 O(n) | ||
| ### sol4.py | ||
| - 時間計算量 O(nm), 空間計算量 O(n) | ||
|
|
||
|
|
||
| 動的計画法 | ||
| https://www.slideshare.net/slideshow/ss-3578511/3578511#1 | ||
|
|
||
| 自分のsol2.pyはもらうDP | ||
|
|
||
| https://github.com/TORUS0818/leetcode/pull/42#discussion_r1904039471 | ||
| > -1 の扱いが気に入らなかったので Generator 使うのは考えてみました。 | ||
|
|
||
| https://github.com/mamo3gr/arai60/blob/322_coin-change/322_coin-change/memo.md | ||
| > 再帰+メモ化で、amount - coin でできる最小枚数をGeneratorで返させて、min(gen, default=-1) で最小値を取る。 自分もそれぞれ配列に入れてから min は思いついたが、最後に配列を舐めるのもなあ…と思って止めた。Generatorなら消費しながらminを取ってくれそう。 | ||
|
|
||
| generatorなら作れない場合の無効値を流さず書くことができる(minを取るときにリストを作る必要もない) | ||
| minにdefaultがあることも知らなかった | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import functools | ||
|
|
||
|
|
||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| @functools.cache | ||
| def minimum_coins(target_amount): | ||
| if target_amount == 0: | ||
| return 0 | ||
| if target_amount < 0: | ||
| return float("inf") | ||
| return min([minimum_coins(target_amount - coin) + 1 for coin in coins]) | ||
|
|
||
| num_changes = minimum_coins(amount) | ||
| return -1 if num_changes == float("inf") else num_changes | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://docs.python.org/3/library/typing.html#typing.List に
とあるように、古いバージョンを扱う場合以外は
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 調べてみるとtypes.GenericAliasというものみたいですね。listに引数を与えてlist[int]とするとtypes.GenericAliasになるようです。 |
||
| num_changes = [float("inf")] * (amount + 1) | ||
| num_changes[0] = 0 | ||
|
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2行目で何となく想像は付くのですが、添字の説明がコメントであると親切に思いました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the minimum number of coins required to make up amount iとしました。確かに初見ではわかりにくいかもしれないです。 |
||
| for target_amount in range(1, amount + 1): | ||
| for coin in coins: | ||
| if target_amount - coin >= 0: | ||
| num_changes[target_amount] = min( | ||
| num_changes[target_amount], | ||
| num_changes[target_amount - coin] + 1, | ||
| ) | ||
|
|
||
| return -1 if num_changes[amount] == float("inf") else num_changes[amount] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import math | ||
|
|
||
|
|
||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| num_changes = [float("inf")] * ( | ||
| amount + 1 | ||
| ) # the minimum number of coins required to make up amount i | ||
| num_changes[0] = 0 | ||
| for target_amount in range(1, amount + 1): | ||
| for coin in coins: | ||
| if target_amount - coin >= 0: | ||
| num_changes[target_amount] = min( | ||
| num_changes[target_amount], | ||
| num_changes[target_amount - coin] + 1, | ||
| ) | ||
|
|
||
| return -1 if math.inf(num_changes[amount]) else num_changes[amount] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| from collections import deque | ||
|
|
||
|
|
||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| minimum_coins = [float("inf")] * (amount + 1) | ||
| minimum_coins[0] = 0 | ||
| coins_sorted = sorted(coins) | ||
|
|
||
| frontier = deque([0]) | ||
| visited = {0} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. visit済みの何なのか分かりにくいので、命名で補足すると良さそうです。 |
||
|
|
||
| while frontier: | ||
| current_sum = frontier.popleft() | ||
| for coin in coins_sorted: | ||
| next_sum = current_sum + coin | ||
| if next_sum in visited or next_sum > amount: | ||
| continue | ||
| minimum_coins[next_sum] = min( | ||
| minimum_coins[next_sum], minimum_coins[current_sum] + 1 | ||
| ) | ||
| frontier.append(next_sum) | ||
| visited.add(current_sum) | ||
|
|
||
| return -1 if minimum_coins[amount] == float("inf") else minimum_coins[amount] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| from collections import deque | ||
| import math | ||
|
|
||
|
|
||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| minimum_coins = [float("inf")] * (amount + 1) | ||
| minimum_coins[0] = 0 | ||
| coins_sorted = sorted(coins) | ||
|
|
||
| frontier = deque([0]) | ||
| visited_sum = {0} | ||
|
|
||
| while frontier: | ||
| current_sum = frontier.popleft() | ||
| for coin in coins_sorted: | ||
| next_sum = current_sum + coin | ||
| if next_sum in visited_sum or next_sum > amount: | ||
| continue | ||
| minimum_coins[next_sum] = min( | ||
| minimum_coins[next_sum], minimum_coins[current_sum] + 1 | ||
| ) | ||
| frontier.append(next_sum) | ||
| visited_sum.add(current_sum) | ||
|
|
||
| return -1 if math.isinf(minimum_coins[amount]) else minimum_coins[amount] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| from collections import deque | ||
|
|
||
|
|
||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| if amount == 0: | ||
| return 0 | ||
|
|
||
| coins_sorted = sorted(coins) | ||
| visited = [False] * (amount + 1) | ||
| visited[0] = True | ||
|
|
||
| frontier = deque([0]) | ||
| steps = 0 | ||
|
|
||
| while frontier: | ||
| steps += 1 | ||
| len_current_frontier = len(frontier) | ||
| for _ in range(len_current_frontier): | ||
| current_sum = frontier.popleft() | ||
| for coin in coins_sorted: | ||
| next_sum = current_sum + coin | ||
| if next_sum > amount: | ||
| break | ||
| if visited[next_sum]: | ||
| continue | ||
| if next_sum == amount: | ||
| return steps | ||
| visited[next_sum] = True | ||
| frontier.append(next_sum) | ||
|
|
||
| return -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
math.isinf() も選択肢としてありそうですね。infinityは普通の値と扱いが異なるので、個人的には値をそのまま比べるよりも
math.isinf()を使いたい気持ちがあります、が好みの問題だと思います。Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
専用の関数がある場合には使うべきですね。math.isinf()を以降覚えておこうと思います。