Implementation of one-dimensional rain algorithm
https://leetcode-cn.com/problems/trapping-rain-water/
def trap_rain_water(height): # Idea: whether a grid can receive rainwater and how much rainwater it can receive are determined by the highest "wall" on both sides # Traverse each grid from the second position to the penultimate one, and find the highest wall on both sides of each grid # If the grid is higher than or equal to the wall height, it cannot catch the rainwater. If it is lower than the wall, it can catch the rainwater from the lower wall minus the grid height # Add up the rainwater received by each grid, and the total amount of rainwater will be obtained after traversing if not height: return 0 rain_water_count = 0 left_max = height[0] right_max = max(height[1:]) for index, high in enumerate(height[1: -1], 1): if left_max < high: left_max = high if high == right_max: right_max = max(height[index + 1:]) continue if high == right_max: right_max = max(height[index + 1:]) continue if left_max > high and right_max > high: lower_wall = left_max if left_max < right_max else right_max rain_water_count += lower_wall - high return rain_water_count if __name__ == '__main__': ret = trap_rain_water2([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) print(ret) ret = trap_rain_water([4, 2, 0, 3, 2, 5]) print(ret)
Implementation of two-dimensional rain connection algorithm
https://leetcode-cn.com/problems/trapping-rain-water-ii/submissions/
Train of thought 1, positive train of thought
- The outermost grid must not receive rain. You can look at the outermost fence and traverse each grid from the inner circle
- When traversing grid a, check whether the height in its four directions is higher than that of A. if the first direction is higher than a, traverse this direction and record the height
- If the second direction is lower than A or equal in height, judge whether the second direction is A boundary. If it is A boundary, grid A must not receive water, and mark grid A as checked
- If the second direction is lower than A or equal in height and is not A boundary, grid B in the second direction wants to compare the remaining three directions (because one direction is A, it cannot be compared back)
- Repeat the appeal for two parts until you can confirm whether grid A can receive water
- If A cannot receive water, all grids higher than or equal to A must not receive water on the footprint traversed by A, and all of them are marked as checked
- If A can receive water, calculate the amount of water A receives. On the footprint traversed by A, all the second equal to A can receive water and calculate the amount of water
- After traversing all inner circle grids, check whether there is grid receiving water. If there is no grid, the traversal is completed. If there is, continue the second traversal, and the subsequent traversal can directly skip the checked grid
class Solution(object): def createFullStateMap(self, heightMap): full_state_map = [] for row in heightMap: full_state_map.append([False] * len(row)) first_row = full_state_map[0] for i, _ in enumerate(first_row): first_row[i] = True last_row = full_state_map[-1] for i, _ in enumerate(last_row): last_row[i] = True for row in full_state_map[1: -1]: row[0] = row[-1] = True self.full_state_map = full_state_map def createWaterWalkMap(self): self.water_walk_map = set() def _clean_water_walk_map(self): self.water_walk_map.clear() def _get_four_direction_coordinates(self, i, j): return [ (i - 1, j), (i, j + 1), (i + 1, j), (i, j - 1) ] def _set_full_state(self): for row, col in self.water_walk_map: if self.height_map[row][col] >= self.cur_high: self.full_state_map[row][col] = True def _fill_water(self): for row, col in self.water_walk_map: coord_high = self.height_map[row][col] if coord_high <= self.lower_wall: self.collected_walter += self.lower_wall - coord_high self.height_map[row][col] = self.lower_wall def _fill_lower_than_bord(self): for row in range(1, self.rows_cnt - 1): for col in range(1, self.cols_cnt - 1): cur_high = self.height_map[row][col] if cur_high < self.min_bord: self.collected_walter += self.min_bord - cur_high self.height_map[row][col] = self.min_bord def _is_on_bord(self, i, j): if i == 0 or i == self.rows_cnt - 1 or j == 0 or j == self.cols_cnt - 1: return True return False def _real_walk(self, i, j): if (i, j) in self.water_walk_map: return self.water_walk_map.add((i, j)) coord_high = self.height_map[i][j] if coord_high > self.cur_high: self.lower_wall = coord_high if not self.lower_wall else min(self.lower_wall, coord_high) return on_bord = self._is_on_bord(i, j) if on_bord: self.can_fill_water = False return four_direction_coordinates = self._get_four_direction_coordinates(i, j) for coord in four_direction_coordinates: self._real_walk(*coord) def _exec_walk(self, i, j): self.water_walk_map.add((i, j)) four_direction_coordinates = self._get_four_direction_coordinates(i, j) for coord in four_direction_coordinates: self._real_walk(*coord) if not self.can_fill_water: self._set_full_state() return self._fill_water() def _get_min_bord(self): min_bord = min(self.height_map[0] + self.height_map[-1]) for row in self.height_map[1: -1]: min_bord = min(min_bord, row[0], row[-1]) return min_bord def _get_max_high(self): max_high = -1 for row in self.height_map: max_high = max(row + [max_high]) return max_high def _water_walk(self): walter_full = True self.min_bord = self._get_min_bord() self.max_high = self._get_max_high() self._fill_lower_than_bord() if self.min_bord == self.max_high: return walter_full for i in range(1, self.rows_cnt): for j in range(1, self.cols_cnt): if self.full_state_map[i][j]: continue self.cur_high = self.height_map[i][j] self.lower_wall = None self.can_fill_water = True self._clean_water_walk_map() self._exec_walk(i, j) if not self.can_fill_water: continue walter_full = False return walter_full def trapRainWater(self, heightMap): """ :type heightMap: List[List[int]] :rtype: int """ self.collected_walter = 0 self.rows_cnt = len(heightMap) self.cols_cnt = len(heightMap[0]) self.height_map = heightMap if self.rows_cnt <= 2 or self.cols_cnt <= 2: return self.collected_walter self.createFullStateMap(heightMap) self.createWaterWalkMap() while True: walter_full = self._water_walk() if walter_full: return self.collected_walter def test_func(self): # func = self.trapRainWater func = foo if callable(func): print(self.__class__.__name__, func.__name__) def foo(): print('hello') if __name__ == "__main__": solution = Solution() lst = [ [1, 4, 3, 1, 3, 2], [3, 2, 1, 3, 2, 4], [2, 3, 3, 2, 3, 1] ] # print(solution.trapRainWater(lst)) solution.test_func()
Train of thought 2, reverse train of thought
- The outermost grid must not receive rain. Put the height and coordinates of the outermost grid into a data structure C as a combination
- Obtain the coordinate a of the grid with the smallest height in C and remove a from C. Because grid a is in the outermost layer, the grid next to him that is shorter than him must be able to receive water, and the height of the received water a minus the height of the grid. After receiving, fill the height of the shorter grid with the height of A
- The lattice higher than A next to A must not receive water
- Calculate all the grids next to A, fill the current height and coordinates of these grids into data structure C, and mark these grids as checked
- Take out the grid A with the lowest current height from the re C again and compare it with the surrounding grid. If A grid has been checked, it will not be checked again
- If it is not checked, repeat the previous comparison process and put the grid data into C
- Until there is no data in C, the traversal is completed
from heapq import heappush, heappop class Solution(object): def createWall(self): for i in range(self.cols_cnt): last_row = self.rows_cnt - 1 heappush(self.heap, (self.height_map[0][i], (0, i))) heappush(self.heap, (self.height_map[last_row][i], (last_row, i))) self.visited.add((0, i)) self.visited.add((last_row, i)) for i in range(self.rows_cnt): last_col = self.cols_cnt - 1 heappush(self.heap, (self.height_map[i][0], (i, 0))) heappush(self.heap, (self.height_map[i][last_col], (i, last_col))) self.visited.add((i, 0)) self.visited.add((i, last_col)) def _get_four_direction_coordinates(self, i, j): return [ (i - 1, j), (i, j + 1), (i + 1, j), (i, j - 1) ] def _is_out_bord_or_visited(self, i, j): if i <= 0 or i >= self.rows_cnt - 1 or j <= 0 or j >= self.cols_cnt - 1 or (i, j) in self.visited: return True return False def _calc(self): high, item = heappop(self.heap) self.current_lower_wall = max(self.current_lower_wall, high) for row, col in self._get_four_direction_coordinates(*item): if self._is_out_bord_or_visited(row, col): continue cur_high = self.height_map[row][col] if cur_high < self.current_lower_wall: self.collected_walter += self.current_lower_wall - cur_high self.height_map[row][col] = self.current_lower_wall self.visited.add((row, col)) heappush(self.heap, (self.height_map[row][col], (row, col))) def _get_min_bord(self): min_bord = min(self.height_map[0] + self.height_map[-1]) for row in self.height_map[1: -1]: min_bord = min(min_bord, row[0], row[-1]) return min_bord def _get_max_high(self): max_high = -1 for row in self.height_map: max_high = max(row + [max_high]) return max_high def _fill_lower_than_bord(self): for row in range(1, self.rows_cnt - 1): for col in range(1, self.cols_cnt - 1): cur_high = self.height_map[row][col] if cur_high < self.min_bord: self.collected_walter += self.min_bord - cur_high self.height_map[row][col] = self.min_bord def _check_finish(self): self.min_bord = self._get_min_bord() self.max_high = self._get_max_high() self._fill_lower_than_bord() if self.min_bord == self.max_high: return True return False def trapRainWater(self, heightMap): """ :type heightMap: List[List[int]] :rtype: int """ self.collected_walter = 0 self.rows_cnt = len(heightMap) self.cols_cnt = len(heightMap[0]) self.height_map = heightMap self.heap = [] self.visited = set() self.current_lower_wall = -1 if self.rows_cnt <= 2 or self.cols_cnt <= 2: return self.collected_walter if self._check_finish(): return self.collected_walter self.createWall() while self.heap: self._calc() return self.collected_walter if __name__ == "__main__": solution = Solution() lst = [ [1, 4, 3, 1, 3, 2], [3, 2, 1, 3, 2, 4], [2, 3, 3, 2, 3, 1] ] print(solution.trapRainWater(lst))