Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions medium/62/answer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Step1

かかった時間:9min

計算量:gridのマスの数をN(m * n)として

時間計算量:O(N)

空間計算量:O(N)

DP
```python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
# 壁際のマスへの行き方は1通りしかないのでgridの初期化の際に1で埋める
grid = []
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grid = [[0] * n for _ in range(m)]
grid[0][0] = 1

として、 row_i と col_i が 0 でない場合は加算するという書き方もありますが、どちらがシンプルかは微妙なところです。

for row_i in range(m):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suffix の _i は何を表していますか?もし index を表しているのであれば、単に row/col でも十分に意味は伝わると思いますので、消してよいと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indexです。
私も使いながらモヤモヤしていたのですが、今後はrow/colでいこうと思います。
ありがとうございます。

if row_i == 0:
grid.append([1] * n)
continue
grid.append([1] + [0] * (n - 1))

for row_i in range(1, m):
for col_i in range(1, n):
grid[row_i][col_i] = grid[row_i - 1][col_i] + grid[row_i][col_i - 1]

return grid[m - 1][n - 1]
```
思考ログ:
- 小学生の時にやったマスの上と左に1を書いて足していくやつ

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分も思いました

- 問題文につられて```grid```にしたが、```num_ways```とかにした方が情報があって良かったか
- ある座標[i, j]への行き方の総数を記録した二次元配列
- ```grid```の作成と壁際の初期化を同時にやってしまったが、冗長でも分けても良かったかもしれない
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ、私は分けるのに賛成ですね。(趣味の範囲。)
口頭で説明する場合は、「m * n を作って、0 列目を1で埋め、0行目を1で埋める」というと思います。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

念の為、

grid = [[0] * n] * m

が違うものは出来上がるということをコメントしておきます。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。

浅いコピーの話ですね。
https://docs.python.org/ja/3/library/copy.html


組み合わせを計算
```python
from scipy.special import comb

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mathじゃなくてscipyのcombを使っているのはなにか理由があるんですか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あまり深い意味はないのですが、皆さんmathを使っていたのでバラエティをと思いまして。

mathのcombはpython3.8から追加されたようですね。
今回の話とは関係ないですが、scipyのcombは重複順列を求めるオプション(repetition)があるようです。



class Solution:
def uniquePaths(self, m: int, n: int) -> int:
return comb(m - 1 + n - 1, n - 1, exact=True)
```
思考ログ:
- 組み合わせを計算する
- 例えば、m = 3, n = 3の場合
- (↓, ↓, →, →)の並べ方を考えればいい(=4C2)
- scipy.comb
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html
- https://github.com/scipy/scipy/blob/v1.14.1/scipy/special/_basic.py#L2681-L2753
- n-1とm-1の小さい方を入れた方が効率がいいかなと思ったが、実装を見たら(_comb_intの中で)やってるようなので気にしないことに
- https://github.com/scipy/scipy/blob/70455fcdc8c0737ac24b592c74083837ca6fbf72/scipy/special/_comb.pyx#L5

# Step2

講師役目線でのセルフツッコミポイント:

参考にした過去ログなど:
- https://github.com/Ryotaro25/leetcode_first60/pull/39
- https://github.com/seal-azarashi/leetcode/pull/31
- https://github.com/Yoshiki-Iwasa/Arai60/pull/47
- https://github.com/nittoco/leetcode/pull/26
- 再帰
- mathモジュールの話題
- https://github.com/nittoco/leetcode/pull/26/files#r1677082520
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あ、コードを読むのは書くのよりも大事なのでどこかからはやるようにして欲しいです。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい。一応全ての過去ログ(PR)にはリンク先も含め目を通しているのですが、なかなか時間がかかってしまってます。。

読み書きの比率が読みに偏っていること自体は問題ないのだと思うのですが、正直なところを白状すると、いまだに人のコードを読んだりドキュメントを読んだりということが自然に出来ている感覚がなく、(推奨されているから)頑張ってやっている、感が少なからずあります。

これは単純にやり方が悪いのか、マインドセットの問題なのか、解消の糸口が見つからずにいます。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

うーん、PR はともかくプロダクションのコードは誰かが誰かに使ってもらうために作っているものである、という面はありますね。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードを読むでいうと、目的があって、それを確かめるために読む側面があるので、全部目を通すということにこだわるよりかは自分の気になるところだけまずは読んでみるのが良いのかなとも思います。

n-1とm-1の小さい方を入れた方が効率がいいかなと思ったが、実装を見たら(_comb_intの中で)やってるようなので気にしないことに

たとえば、これとかはうまく実践できている例だなと自分は感じました。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そうですね〜、自分も仕事でコード書くときに、急いでたりするとなかなかドキュメントが見れないでいます(データ分析だと、プロダクションコードじゃないので意識が希薄になってしまいがちというのもあるのかも)
仕事だとLeetCodeよりいろんなライブラリのメソッドを使うので、LeetCodeよりさらに多くのドキュメント見る必要があり、難しいですよね。3つに1つくらいはせめて見るようにしたいです、、、

- https://github.com/fhiyo/leetcode/pull/34
- 任意精度演算の話
- https://zenn.dev/yukinarit/articles/afb263bf68fff2
- https://github.com/goto-untrapped/Arai60/pull/32
- https://github.com/YukiMichishita/LeetCode/pull/14
- https://github.com/sakupan102/arai60-practice/pull/34
- combinationsの再帰(というか分解)
- https://github.com/SuperHotDogCat/coding-interview/pull/16
- https://github.com/shining-ai/leetcode/pull/32
- https://github.com/hayashi-ay/leetcode/pull/39
- 末尾再帰

DPの配列を圧縮(空間計算量:O(min(n, m)))
```python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if n < m:
n, m = m, n

num_ways = [1] * n
for row_i in range(1, m):
for col_i in range(1, n):
num_ways[col_i] = num_ways[col_i] + num_ways[col_i - 1]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

step3 のように、 num_ways[col_i] += num_ways[col_i - 1] としたほうがシンプルだと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
他の方のPRで同様のご指摘を拝見して、STEP3で修正しました。


return num_ways[-1]
```
思考ログ:
- 確かに上書きしていけば二次元配列は必要ない
- n, mの小さい方で配列を確保すればよりエコ
- そこまで気にする必要はないかもしれないが、追加の処理も少ないのでやっておいてもいいのではという気持ち

再帰
```python
from functools import cache


class Solution:
@ cache
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)
```
- cacheを取っておかないと厳しい
- ただcacheを取れば万事解決というわけでもないのでちゃんと考えて選択する
- https://github.com/fhiyo/leetcode/pull/34/files

# Step3

かかった時間:2min

```python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
# メモリ節約のため、uniqe path数を管理する配列の長さをmin(n, m)で確保する
if m < n:
n, m = m, n

num_ways = [1] * n
for _ in range(1, m):
for i in range(1, n):
num_ways[i] += num_ways[i - 1]

return num_ways[-1]
Comment on lines +125 to +130
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的にはいきなり1で埋めるのはちょっと抵抗感あるかもという感じで、

num_ways = [0] * n
num_ways[0] = 1
for _ in range(m):
    for i in range(1, n):
        num_ways[i] += num_ways[i - 1]

の方がいいかなと思ったんですが、難しいですね

```
思考ログ:
- 変数名などを修正
- いきなりn, mのスワップが始まるとアレかと思いコメントを入れてみたが、もう少しいい説明があったか

# Step4

```python
```
思考ログ: