-
Notifications
You must be signed in to change notification settings - Fork 0
Rotting Oranges #91
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?
Rotting Oranges #91
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,169 @@ | ||
| # Rotting Oranges | ||
|
|
||
| You are given an m x n grid where each cell can have one of three values: | ||
|
|
||
| 0 representing an empty cell, | ||
| 1 representing a fresh orange, or | ||
| 2 representing a rotten orange. | ||
| Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten. | ||
|
|
||
| Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1. | ||
|
|
||
| Ex | ||
| Input: grid = [[2,1,1],[1,1,0],[0,1,1]] | ||
| Output: 4 | ||
|
|
||
| ## Step1 | ||
|
|
||
| レベル別DFSで処理する。気をつける必要がある点としては | ||
|
|
||
| - キューに入るのは「ちょうど今腐ったオレンジ」なので、全てが腐った後も1回余分にループを回してしまう。なので-1からスタートするか最後に引く必要がある。 | ||
| - 上記の操作をするとき、「一度もループに入らないかつ最初からフレッシュなオレンジが0個」のケースで不整合が生じるのでエッジケースとして処理する | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def orangesRotting(self, grid: List[List[int]]) -> int: | ||
| m, n = len(grid), len(grid[0]) | ||
| current_level = [] | ||
| fresh_count = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == 2: | ||
| current_level.append((i, j)) | ||
| elif grid[i][j] == 1: | ||
| fresh_count += 1 | ||
| if fresh_count == 0: | ||
| return 0 | ||
|
|
||
| minutes = -1 | ||
| direction = [(0, 1), (0, -1), (1, 0), (-1, 0)] | ||
| while current_level: | ||
| next_level = [] | ||
| minutes += 1 | ||
| for r, c in current_level: | ||
| for dr, dc in direction: | ||
| next_r, next_c = r + dr, c + dc | ||
|
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. 1 行で複数の変数に代入しても、あまり読みやすくならないと思います。変数の代入は 1 行ずつ行ったほうが良いと思います。 |
||
| if not (0 <= next_r < m and 0 <= next_c < n): | ||
| continue | ||
|
|
||
| if grid[next_r][next_c] == 2: | ||
| continue | ||
|
|
||
| if grid[next_r][next_c] == 1: | ||
| grid[next_r][next_c] = 2 | ||
| next_level.append((next_r, next_c)) | ||
| fresh_count -= 1 | ||
|
|
||
| current_level = next_level | ||
|
|
||
| if fresh_count != 0: | ||
| return -1 | ||
|
|
||
| return minutes | ||
| ``` | ||
|
|
||
| 次のようにするとエッジケースへの対応が必要なくなる。 | ||
| ループの条件を「直前に腐ったばかりのオレンジが存在する」かつ「まだフレッシュなオレンジが存在する」とすればループから抜けた時点がフレッシュなオレンジがちょうど無くなった時刻となるので、`minutes`をそのままreturnできる | ||
|
|
||
| `grid[next_r][next_c] == 2`の分岐はいらない | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def orangesRotting(self, grid: List[List[int]]) -> int: | ||
| m, n = len(grid), len(grid[0]) | ||
| rotten = [] | ||
| fresh_count = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == 2: | ||
| rotten.append((i, j)) | ||
| elif grid[i][j] == 1: | ||
| fresh_count += 1 | ||
| minutes = 0 | ||
| directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] | ||
| while rotten and fresh_count: | ||
| minutes += 1 | ||
| next_rotten = [] | ||
| for r, c in rotten: | ||
| for dr, dc in directions: | ||
| nr, nc = r + dr, c + dc | ||
| if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == 1: | ||
| grid[nr][nc] = 2 | ||
| fresh_count -= 1 | ||
| next_rotten.append((nr, nc)) | ||
| rotten = next_rotten | ||
|
|
||
| return minutes if fresh_count == 0 else -1 | ||
| ``` | ||
|
|
||
| ## Step2 | ||
|
|
||
| - https://github.com/kazuki-official/leetcode/pull/102/changes | ||
| - ループ中でearly returnすれば最後に1引いたりループの条件を複雑にする必要がなくなる | ||
|
|
||
| ```py | ||
| while rotten_oranges: | ||
| next_rotten_oranges = [] | ||
| minutes += 1 | ||
| for r, c in rotten_oranges: | ||
| for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]: | ||
| if r + dr < 0 or len(grid) <= r + dr or c + dc < 0 or len(grid[0]) <= c + dc: | ||
|
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. Step 1 のように変数に置いたほうが、見通しが良くなると思いました。 next_r = r + dr
next_c = c + dcThere 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. Step 1 のように数直線上に一直線になるように書いたほうが、見通しが良くなると思いました。 |
||
| continue | ||
| if grid[r + dr][c + dc] == FRESH: | ||
| grid[r + dr][c + dc] = ROTTEN | ||
| num_fresh_oranges -= 1 | ||
| if num_fresh_oranges == 0: | ||
| return minutes | ||
| next_rotten_oranges.append((r + dr, c + dc)) | ||
| continue | ||
| rotten_oranges = next_rotten_oranges | ||
| ``` | ||
|
|
||
| - https://github.com/nittoco/leetcode/pull/49/changes | ||
| - `if all(cell == self.EMPTY for row in grid for cell in row):` | ||
| - `if any(cell == self.FRESH for row in rotting_grid for cell in row):` | ||
| - カウントじゃなくて全セルを1行で参照する。allとanyの中身はgenerator | ||
|
|
||
| ## Step3 | ||
|
|
||
| Time: O(m\*n) | ||
| Space: O(m\*n) | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def orangesRotting(self, grid: List[List[int]]) -> int: | ||
| rotten = [] | ||
| m, n = len(grid), len(grid[0]) | ||
| fresh_count = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == 1: | ||
| fresh_count += 1 | ||
| continue | ||
|
|
||
| if grid[i][j] == 2: | ||
| rotten.append((i, j)) | ||
|
|
||
| directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] | ||
| minutes = 0 | ||
| while rotten and fresh_count: | ||
| next_rotten = [] | ||
| for r, c in rotten: | ||
| for dr, dc in directions: | ||
| next_r, next_c = r + dr, c + dc | ||
| if not (0 <= next_r < m and 0 <= next_c < n): | ||
| continue | ||
|
|
||
| if grid[next_r][next_c] == 1: | ||
| grid[next_r][next_c] = 2 | ||
| next_rotten.append((next_r, next_c)) | ||
| fresh_count -= 1 | ||
|
|
||
| rotten = next_rotten | ||
| minutes += 1 | ||
|
|
||
| if fresh_count == 0: | ||
| return minutes | ||
|
|
||
| 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.
1 と 2 がマジックナンバーになっているのが気になりました。 FRESH と ROTTING などの定数に置くと良いと思いました。
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.
そちらの方がわかりやすいですね!