-
Notifications
You must be signed in to change notification settings - Fork 0
LeetCode 62. Unique Paths #57
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?
Changes from all commits
1af0458
ea9e55c
5715984
e92e59c
fd98c32
23b9ac7
e06ef0f
2f2f7cd
29fcf77
5f5766d
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,80 @@ | ||
| # Step 1 | ||
|
|
||
| 典型的なDPの問題 | ||
|
|
||
| 素直に `m * n` の grid を用意して埋めていけばいい。(`step1.py`) | ||
| 時間計算量: O(mn), 空間計算量: O(mn) | ||
|
|
||
| `m * n` 全ての結果を持つ必要はなくて、次のrowを埋めるのに必要なのは直前のrowだけなので、2つのrowだけ持てばいい。(`step1_two_rows.py`) | ||
| 時間計算量は O(mn) で変わらずだが、空間計算量は O(m) で済む。m と n を入れ替えても結果は変わらないので、nの方が小さいことがわかっている場合は先にm と n を入れ替えたら良さそう。 | ||
|
|
||
| > `1 <= m, n <= 100` | ||
|
|
||
| なので、最悪でも `10^4` くらいのオーダーの処理しか必要ないので、実行時間は問題ないだろう。 | ||
| Pythonのintのメモリ使用量は後で調べるとして、Cだったら int が多くの場合 32 bits = 4 bytes で、それが `10^4` 個あると `4 * 10^4 bytes ~= 39KB`か (vectorのオーバーヘッドを無視して)。 | ||
|
|
||
| 具体的な実装はまだ詳しくみれていないが、[Python Maven - Size of integer in Python](https://python.code-maven.com/size-of-integer-in-python) を見ると | ||
|
|
||
| ```python | ||
| import sys | ||
|
|
||
| for i in range(0, 200, 10): | ||
| num = 2**i | ||
| print(sys.getsizeof(num), num) | ||
|
|
||
| """ | ||
| 28 1 | ||
| 28 1024 | ||
| 28 1048576 | ||
| 32 1073741824 | ||
| 32 1099511627776 | ||
| 32 1125899906842624 | ||
| 36 1152921504606846976 | ||
| 36 1180591620717411303424 | ||
| 36 1208925819614629174706176 | ||
| 40 1237940039285380274899124224 | ||
| 40 1267650600228229401496703205376 | ||
| 40 1298074214633706907132624082305024 | ||
| 44 1329227995784915872903807060280344576 | ||
| 44 1361129467683753853853498429727072845824 | ||
| 44 1393796574908163946345982392040522594123776 | ||
| 48 1427247692705959881058285969449495136382746624 | ||
| 48 1461501637330902918203684832716283019655932542976 | ||
| 48 1496577676626844588240573268701473812127674924007424 | ||
| 52 1532495540865888858358347027150309183618739122183602176 | ||
| 52 1569275433846670190958947355801916604025588861116008628224 | ||
| """ | ||
| ``` | ||
|
|
||
| のようなテストが提示されていて、`1` のような小さい数字でも 28 bytes も使用していることがわかった。 | ||
|
|
||
| # Step 2 | ||
|
|
||
| ## 組み合わせ | ||
|
|
||
| [dxxsxsxkxさんのPR](https://github.com/dxxsxsxkx/leetcode/pull/33/changes#diff-e425ab53cee19df2ef6995889ba4e527fc0e29e480a6787c15119f2da066511c) | ||
|
|
||
| 動的計画法だと初めからわかっていたのでそれに飛びついてしまったが、いつかの数学でやった組み合わせの問題であることも連想できた方が良かったな。 | ||
|
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. これは僕も同じことしました。 |
||
|
|
||
| 総移動距離が (m + n - 2) で、そこから m - 1 (か n - 1) を置く場所を選ぶから (m + n - 2)C(m - 1)。 | ||
| 例えばm = 3, n = 4 だったら 2個のdownと3個のright を組み合わせるから5箇所から2個のdown を選んで (例えば DRDRR) 5C2 = 10。 | ||
|
|
||
| 書いてみた -> `step2_combination.py` Pythonだから桁を気にせず掛け合わせてしまっている。 | ||
|
|
||
| 時間計算量: O(min(m, n)), 空間計算量: O(1) | ||
|
|
||
| そもそもPythonには [math.comb](https://docs.python.org/3/library/math.html#math.comb) という関数があるらしいのでそれを使えば簡単か。-> `step2_math_comb.py` | ||
|
|
||
| ## Memoization | ||
|
|
||
| そういえば、Memoizationでもいけそうだな -> `step2_memoization.py` | ||
|
|
||
| ## 1 列 DP | ||
|
|
||
| [naoto-iwaseさんのPR](https://github.com/naoto-iwase/leetcode/pull/38/changes#diff-8a6ff63343e74cc80d732f8899ddf515ccf1d52a91f0f73f0d8b022fd23400cbR44) | ||
|
|
||
| 保持するのが一列だけでも良いのか、いや確かに言われてみれば。-> `step2_one_row_dp.py` | ||
|
|
||
| # Step 3 | ||
|
|
||
| 特に工夫もないTabulationが一番わかりやすい。 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| path_count = [[0] * n for _ in range(m)] | ||
| for i in range(m): | ||
| path_count[i][0] = 1 | ||
| for i in range(n): | ||
| path_count[0][i] = 1 | ||
|
|
||
| for row in range(1, m): | ||
| for col in range(1, n): | ||
| path_count[row][col] = ( | ||
| path_count[row - 1][col] + path_count[row][col - 1] | ||
| ) | ||
|
|
||
| return path_count[m - 1][n - 1] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| prev_row = [1] * n | ||
| for row in range(1, m): | ||
| curr_row = [0] * n | ||
| for col in range(n): | ||
| if col == 0: | ||
| curr_row[col] = 1 | ||
| continue | ||
|
|
||
| curr_row[col] = curr_row[col - 1] + prev_row[col] | ||
|
|
||
| prev_row = curr_row | ||
|
|
||
| return prev_row[-1] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| count = [[0] * n for _ in range(m)] | ||
| for row in range(m): | ||
| for col in range(n): | ||
| if row == 0 or col == 0: | ||
| count[row][col] = 1 | ||
| continue | ||
|
|
||
| count[row][col] = count[row - 1][col] + count[row][col - 1] | ||
|
|
||
| return count[m - 1][n - 1] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| m, n = max(m, n), min(m, n) | ||
|
|
||
| numerator = 1 | ||
| denominator = 1 | ||
| for i in range(n - 1): | ||
| numerator *= m + n - 2 - i | ||
| denominator *= n - 1 - i | ||
|
|
||
| return numerator // denominator |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import math | ||
|
|
||
|
|
||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| return math.comb(m + n - 2, m - 1) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import functools | ||
|
|
||
|
|
||
| class Solution: | ||
| @functools.cache | ||
|
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. pythonにはこんなのがあるんですね.便利すぎてびっくりしました. |
||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| if m == 1 or n == 1: | ||
| return 1 | ||
| return self.uniquePaths(m - 1, n) + self.uniquePaths(m, n - 1) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| count = [1] * n | ||
| for _ in range(1, m): | ||
| for col in range(1, n): | ||
| count[col] += count[col - 1] | ||
| return count[-1] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| class Solution: | ||
| def uniquePaths(self, m: int, n: int) -> int: | ||
| count = [[0] * n for _ in range(m)] | ||
| for row in range(m): | ||
| for col in range(n): | ||
| if row == 0 or col == 0: | ||
|
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. 読み直すと、上端か左端かの2つのチェックが一行にまとまっているのがちょっとわかりづらい? 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. 個人的にはこちらの方が読みやすいです.できるだけ同じ枠組みに入れてしまうのが好きと言うのもあるかもしれません.
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. ありがとうございます、他の方々との感覚のすり合わせができて助かります! 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. 僕は前者の方が目的が明確で読みやすく感じましたが、好みのレベルだと思います。どちらも読みやすいです! 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. こういう書き方もあるのかと勉強になりました 🙏 |
||
| count[row][col] = 1 | ||
| continue | ||
|
|
||
| count[row][col] = count[row - 1][col] + count[row][col - 1] | ||
|
|
||
| return count[m - 1][n - 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.
さらに言えば,rowの更新は左から右の一方向なので1つのrowを持てば十分ですね.つまり
current_row[col]を更新する直前,current_row[col]にはprev_row[col]と同じ値が入っているはずなのでと更新できます.(可読性は直前と今で別変数に入れた方が良いでしょうが)