Conversation
| if not obstacleGrid or not obstacleGrid[0]: | ||
| return 0 | ||
|
|
||
| m, n = len(obstacleGrid), len(obstacleGrid[0]) |
There was a problem hiding this comment.
m, n は row, col との対応が分かりにくいので、num_rows, num_cols などと置くといいのではないでしょうか。
| unique_paths_per_row = [1 - o for o in obstacleGrid[0]] | ||
| for col in range(1, n): | ||
| unique_paths_per_row[col] *= unique_paths_per_row[col - 1] |
There was a problem hiding this comment.
obstacleGrid の要素が 0 あるいは 1 であることを利用した算術による初期化で、ちょっとトリッキーに感じました。例えば障害物の有無を文字列で表すようになったときなど、仕様変更にも弱いように見えます。私がいまパッと書け、と言われたら次のようにします。
| unique_paths_per_row = [1 - o for o in obstacleGrid[0]] | |
| for col in range(1, n): | |
| unique_paths_per_row[col] *= unique_paths_per_row[col - 1] | |
| EMPTY = 0 | |
| if obstacleGrid[0][0] != EMPTY: | |
| return 0 | |
| unique_paths_per_row = [1] + [0] * (n - 1) | |
| for col in range(1, n): | |
| if obstacleGrid[0][col] != EMPTY: | |
| break | |
| unique_paths_per_row[col] = unique_paths_per_row[col - 1] |
偉そうなことを言いながらあんまり上手く書けている気もしないのですが、「左隣 col-1 を引き継ぐ。ただし障害物があったらそこから先はゼロ」感が出るとよいのではないでしょうか。
かなり書き方の幅が広いように思うので、もうちょっと他の人の書き方を漁ってみても良さそうです。
There was a problem hiding this comment.
アドバイスを参考にして以下のように書き直しました。(ほぼ同じですが)
EMPTY = 0
if obstacleGrid[0][0] != EMPTY:
return 0
unique_paths_per_row = [1] + [0] * (num_cols - 1)
col = 1
while col < num_cols and obstacleGrid[0][col] == EMPTY:
unique_paths_per_row[col] = 1
col += 1| unique_paths_per_row[col] = 0 | ||
| continue | ||
|
|
||
| ways = unique_paths_per_row[col] |
There was a problem hiding this comment.
path と way は同じ意味で使っているので、英単語も揃えたほうが分かりやすそうです。
| if col + 1 < n_col and not obstacleGrid[row][col + 1]: | ||
| unique_paths_per_row[col + 1] += ways | ||
| if row + 1 < n_row and not obstacleGrid[row + 1][col]: | ||
| unique_paths_next_row[col] += ways |
There was a problem hiding this comment.
配る相手セルが障害物で潰れているかどうかは、ここでは気にしなくて良さそうです。
| if col + 1 < n_col and not obstacleGrid[row][col + 1]: | |
| unique_paths_per_row[col + 1] += ways | |
| if row + 1 < n_row and not obstacleGrid[row + 1][col]: | |
| unique_paths_next_row[col] += ways | |
| if col + 1 < n_col: | |
| unique_paths_per_row[col + 1] += ways | |
| if row + 1 < n_row: | |
| unique_paths_next_row[col] += ways |
There was a problem hiding this comment.
なるほど、配る側のセルになったときに0になるので処理が重複していましたね
There was a problem hiding this comment.
メモリ使用量が増えるのをやむなしで、2次元配列(フルのテーブル)で書いたほうが、配るDPは分かりやすいと思いました。また、最終行は配らなくていいとか、障害物があったら0に潰して配れない(こっちは最終行も処理する必要がある)、など制御が比較的面倒で、配るよりも貰う方が書きやすそうに感じました。
There was a problem hiding this comment.
「配るよりも貰う方が書きやすそう」は同意します。
配るDPでも直前の二行を保持しておけば良いので、一次元にする必要はないのではないかとと個人的には思います。
There was a problem hiding this comment.
配るDPでも直前の二行を保持しておけば良いので、一次元にする必要はない
そのとおりで、処理的には問題ないのですが、書きやすさ(バグの埋め込みやすさ)・読みやすさの観点からは、2次元配列の方が優れていそうだな、と感じました。unique_paths_per_row と unique_paths_next_row の入れ替え(更新)の必要がなかったり、最終行は入れ替えしないという条件が理解しやすかったりしそうです。
| col = 1 | ||
| while col < num_cols and obstacleGrid[0][col] == EMPTY: | ||
| unique_paths_per_row[col] = 1 | ||
| col += 1 |
There was a problem hiding this comment.
好みの問題ですがcolがこのwhile内でしか使われていないのでfor文にしても良いと思いました.
for col in range(1, num_cols):
if obstacleGrid[0][col] != EMPTY:
break
unique_paths_per_row[col] = 1There was a problem hiding this comment.
なるほど、その書き方もありますね。
ただ今回は「while の条件式の中にループが続く条件を書きたい」という意図で現状のものを採用しようと思います。
| col = 1 | ||
| while col < num_cols and obstacleGrid[0][col] == EMPTY: | ||
| unique_paths_per_row[col] = 1 | ||
| col += 1 |
There was a problem hiding this comment.
というかこの箇所なくして後ろのrowに関するforループの範囲を以下のようにすれば動くのではないでしょうか.
for row in range(0, num_rows):あるマスの値が,その左のマスの値と上のマスの値の和で決まるという仕組みからすると,コメント先の部分の処理を後ろのforループに含んでしまうのも不自然ではないと感じます.
There was a problem hiding this comment.
ああ、その通りですね。
sol2.pyでその点を改善した初期化にしたようです。
| n_row, n_col = len(obstacleGrid), len(obstacleGrid[0]) | ||
|
|
||
| unique_paths_per_row = [0] * n_col | ||
| unique_paths_per_row[0] = 0 if obstacleGrid[0][0] else 1 |
There was a problem hiding this comment.
obstacleGrid[0][0] == 0の時はsol1.pyのようにearly returnする方が無駄なforループを回さないで済むので好きです.
There was a problem hiding this comment.
そうですね。分かりやすさの意味でもその方が良いと思うので採用させていただきます。
(書いた時にはまとめて処理できるが良いと考えたんだと思います)
| if obstacleGrid[row][col]: | ||
| unique_paths_per_row[col] = 0 | ||
| continue | ||
| elif col > 0: | ||
| unique_paths_per_row[col] += unique_paths_per_row[col - 1] |
There was a problem hiding this comment.
正誤には関係ありませんが,if ... elif ... の形でその後にコードが登場しない場合,if分岐内でのconitnueは不要ですね.
(今後コードを書き加える場合,continueすべきケースとすべきでないケースの両方があり得そうなので特段continueを入れる必要性は感じられませんでした.)
There was a problem hiding this comment.
そのとおりですね。If...continueとするならelifではなくifの方が適切ですね。
今回はcontinueを消そうと思います。
| for row in range(n_row): | ||
| unique_paths_next_row = [0] * n_col | ||
| for col in range(n_col): | ||
| if obstacleGrid[row][col]: | ||
| unique_paths_per_row[col] = 0 | ||
| continue | ||
|
|
||
| paths = unique_paths_per_row[col] | ||
| if paths == 0: | ||
| continue | ||
|
|
||
| if col + 1 < n_col: | ||
| unique_paths_per_row[col + 1] += paths | ||
| if row + 1 < n_row: | ||
| unique_paths_next_row[col] += paths | ||
|
|
||
| if row + 1 < n_row: | ||
| unique_paths_per_row = unique_paths_next_row |
There was a problem hiding this comment.
これは単に好みの問題ですが,sol2.pyで行ったような更新の方が分岐が少なくわかりやすく感じました.
There was a problem hiding this comment.
同意します。自分が素直に思いついたのもsol2.pyの方でした。
sol3.pyは配るDPの練習として書きました。
https://leetcode.com/problems/unique-paths-ii/