diff --git a/994. Rotting Oranges.md b/994. Rotting Oranges.md new file mode 100644 index 0000000..38de770 --- /dev/null +++ b/994. Rotting Oranges.md @@ -0,0 +1,191 @@ +### Step1 + +- 内包表記を多用してみた。どうだろうか。 + - 意図は明確になるように感じるけど、長くなりがち? +- minites_plus_oneは流石にわかりにくいかも。 + - while oranges_rottenのすぐ後でminites_plus_one += 1したいなあと思ってたらこうなってしまった。 +- あとで気づいたが、xとyが逆でした + +```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] + 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 +``` + +## 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]]]: + num_rotten = 0 + coordinates_rotten = set() + for y in range(len(grid)): + 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): + 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): + 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 +```