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
191 changes: 191 additions & 0 deletions 994. Rotting Oranges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
### Step1

- 内包表記を多用してみた。どうだろうか。
- 意図は明確になるように感じるけど、長くなりがち?
- minites_plus_oneは流石にわかりにくいかも。
- while oranges_rottenのすぐ後でminites_plus_one += 1したいなあと思ってたらこうなってしまった。
- あとで気づいたが、xとyが逆でした

Choose a reason for hiding this comment

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

個人的にはx,yで命名すると結構混乱する気がするんですよね。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.

ありがとうございます。rowとcolも自分は結構混乱するんですよね、、、(が、これは自分が覚えた方が良い気がしてきました)


```python
class Solution:
EMPTY = 0
FRESH = 1
ROTTEN = 2

def orangesRotting(self, grid: List[List[int]]) -> int:
rotting_grid = [row[:] for row in grid]

def rot_orange_if_fresh(x: int, y: int) -> None:
if not 0 <= x < len(grid) or not 0 <= y < len(grid[0]):
return
if rotting_grid[x][y] != self.FRESH:
return
rotting_grid[x][y] = self.ROTTEN
rotting_oranges.append((x, y))

minites_plus_one = 0
if all(cell == self.EMPTY for row in grid for cell in row):
return 0
oranges_rotten = [(x, y) for y in range(len(grid[0])) for x in range(len(grid)) if grid[x][y] == self.ROTTEN]

Choose a reason for hiding this comment

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

英語の語順的にはrotten_orangesになるかなと思います

while oranges_rotten:
minites_plus_one += 1
rotting_oranges = []
for x, y in oranges_rotten:
rot_orange_if_fresh(x + 1, y)
rot_orange_if_fresh(x - 1, y)
rot_orange_if_fresh(x, y + 1)
rot_orange_if_fresh(x, y - 1)
oranges_rotten = rotting_oranges
if any(cell == self.FRESH for row in rotting_grid for cell in row):
return -1
return minites_plus_one - 1
```
Copy link

Choose a reason for hiding this comment

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

時間を進めるタイミングが難しいですね。素直に問題文の通りに表現すると

すべてのオレンジが腐っているかを確認して、その場合は return
時間を進めて、隣のオレンジを腐らせる。
状況が変わっていなかったら return -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.

ありがとうございます。確かにこれが自然ですね

# 略

    def orangesRotting(self, grid: List[List[Orange]]) -> int:
        minutes = 0
        rotten_oranges_so_far = set([(x, y) for y in range(len(grid)) for x in range(len(grid[0])) if grid[y][x] == Orange.ROTTEN])
        num_oranges_all = sum(grid[y][x] != Orange.EMPTY for y in range(len(grid)) for x in range(len(grid[0])))
        while True:
            if len(rotten_oranges_so_far) == num_oranges_all:
                return minutes
            new_rotten_oranges = self._rot_next_oranges(grid, rotten_oranges_so_far)
            minutes += 1
            if len(new_rotten_oranges) == 0:
                return -1
            rotten_oranges_so_far |= new_rotten_oranges

これもアリかなと思ってました
増やせなかったときに、なのに全部腐らせることができなかったら-1

# 略
 
     def orangesRotting(self, grid: List[List[Orange]]) -> int:
     minutes = 0
     rotten_oranges_so_far = set([(x, y) for y in range(len(grid)) for x in range(len(grid[0])) if grid[y][x] == Orange.ROTTEN])
     num_oranges_all = sum(grid[y][x] != Orange.EMPTY for y in range(len(grid)) for x in range(len(grid[0])))
     while True:
         new_rotten_oranges = self._rot_next_oranges(grid, rotten_oranges_so_far)
         if len(new_rotten_oranges) == 0:
             if len(rotten_oranges_so_far) < num_oranges_all:
                 return -1
             return minutes
         rotten_oranges_so_far |= new_rotten_oranges
         minutes += 1


## Step2

- deepcopyする必要なかった、、、
- 以下の悩んだポイント
- いくらなんでも繰り返し多すぎ?ただ、関数化するとしてもどこまで関数化するかが難しい。
- x, yはdataclassとかにした方がいいだろうか、大袈裟?

```python
class Solution:
EMPTY = 0
FRESH = 1
ROTTEN = 2

def get_initial_rotten_oranges_num_and_coordinates(self, grid: List[List[int]]) -> Tuple[int, Set[Tuple[int, int]]]:
Copy link

Choose a reason for hiding this comment

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

protected method のため、先頭に _ を付けてあげるとよいと思いました。

num_rotten = 0
coordinates_rotten = set()
for y in range(len(grid)):
Copy link

Choose a reason for hiding this comment

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

len(grid)len(grid[0]) が複数回登場するため、それぞれ num_rows, num_columns に代入してあげると読みやすくなると思いました。

for x in range(len(grid[0])):
if grid[y][x] != self.ROTTEN:
continue
num_rotten += 1
coordinates_rotten.add((x, y))
return num_rotten, coordinates_rotten

def count_oranges(self, grid: List[List[int]]) -> int:
num_oranges = 0
for y in range(len(grid)):
for x in range(len(grid[0])):
if grid[y][x] == self.EMPTY:
continue
num_oranges += 1
return num_oranges

def is_fresh(self, x: int, y: int, grid: List[List[int]], coordinates_rotten: Set[Tuple[int, int]]) -> bool:
if not 0 <= x < len(grid[0]) or not 0 <= y < len(grid):
Copy link

Choose a reason for hiding this comment

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

if not (0 <= x < len(grid[0]) and 0 <= y < len(grid)): のほうがややシンプルだと思いました。

return False
if grid[y][x] == self.EMPTY:
return False
if (x, y) in coordinates_rotten:
return False
return True

def orangesRotting(self, grid: List[List[int]]) -> int:
num_rotten, coordinates_rotten = self.get_initial_rotten_oranges_num_and_coordinates(grid)
num_all_oranges = self.count_oranges(grid)
previously_rotten = list(coordinates_rotten)
result = 0
while True:
rotting = []
if num_rotten == num_all_oranges:
return result
if not previously_rotten:
return -1
for x, y in previously_rotten:
if self.is_fresh(x + 1, y, grid, coordinates_rotten):
Copy link

Choose a reason for hiding this comment

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

deltas = [(0, 1), (0, -1), (1, 0), (-1, 0)]
...
for delta in deltas:
    if (self.is_fresh(x + delta[0], y + delta[1], grid, coordinates_rotten)):

といった感じにすると、コードの重複が減ると思いました。

num_rotten += 1
coordinates_rotten.add((x + 1, y))
rotting.append((x + 1, y))
if self.is_fresh(x - 1, y, grid, coordinates_rotten):
num_rotten += 1
coordinates_rotten.add((x - 1, y))
rotting.append((x - 1, y))
if self.is_fresh(x , y + 1, grid, coordinates_rotten):
num_rotten += 1
coordinates_rotten.add((x , y + 1))
rotting.append((x , y + 1))
if self.is_fresh(x, y - 1, grid, coordinates_rotten):
num_rotten += 1
coordinates_rotten.add((x , y - 1))
rotting.append((x , y - 1))
previously_rotten = rotting
result += 1

```

https://discord.com/channels/1084280443945353267/1200089668901937312/1201562858295656599

- 上の自分のコードは、関係性がある(lenとか差集合とかの関連がある)変数が多い割にそれぞれ別で処理しているので、追うのが大変になってるかも。変数管理が大変?
- set()のメソッドを読む
- https://docs.python.org/3/library/stdtypes.html#set
- remove(elem)とかdiscard(elem)とか知らなかった(昔見たかもしれないが、忘れていた)
- 前者はelemがない場合ValueError, 後者は単に無視
- IntEnumが便利そう
- 0, 1, 2以外が入っても、型チェックをonにしたら弾いてくれる
- num_rottenは、get_initial_oranges_of_rottenで計算せずに、単にlen(rotten_coordinates)でよかったかも

```python
from enum import IntEnum

class Orange(IntEnum):
EMPTY = 0
FRESH = 1
ROTTEN = 2

class Solution:
def is_fresh(self, grid: List[List[Orange]], x: int, y: int, rotten_so_far: Set[Tuple[int, int]]) -> bool:
if not 0 <= x < len(grid[0]) or not 0 <= y < len(grid):
return False
if grid[y][x] == Orange.EMPTY:
return False
if (x, y) in rotten_so_far:
return False
return True

def count_oranges(self, grid: List[List[Orange]]) -> int:
num_oranges = 0
for y in range(len(grid)):
for x in range(len(grid[0])):
if grid[y][x] == Orange.EMPTY:
continue
num_oranges += 1
return num_oranges

def get_initial_oranges_of_rotten(self, grid: List[List[Orange]]) -> Tuple[int, Set[Tuple[int, int]]]:
rotten_coordinates = set()
num_rotten = 0
for y in range(len(grid)):
for x in range(len(grid[0])):
if grid[y][x] != Orange.ROTTEN:
continue
rotten_coordinates.add((x, y))
num_rotten += 1
return num_rotten, rotten_coordinates

def orangesRotting(self, grid: List[List[Orange]]) -> int:
num_rotten, rotten_coordinates = self.get_initial_oranges_of_rotten(grid)
previous_rotten = rotten_coordinates.copy()
num_oranges = self.count_oranges(grid)
minutes = 0
while num_rotten < num_oranges:
rotting = set()
for x, y in previous_rotten:
if self.is_fresh(grid, x + 1, y, rotten_coordinates):
rotting.add((x + 1, y))
if self.is_fresh(grid, x - 1, y, rotten_coordinates):
rotting.add((x - 1, y))
if self.is_fresh(grid, x, y + 1, rotten_coordinates):
rotting.add((x, y + 1))
if self.is_fresh(grid, x, y - 1, rotten_coordinates):
rotting.add((x, y - 1))
if not rotting:
return -1 # impossible
rotten_coordinates |= rotting
num_rotten += len(rotting)
previous_rotten = rotting
minutes += 1
return minutes
```