# Implementation of rain algorithm

Posted by danoli3 on Thu, 17 Feb 2022 17:00:08 +0100

### 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
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
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 = 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

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):

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 + self.height_map[-1])
for row in self.height_map[1: -1]:
min_bord = min(min_bord, row, 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)
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[i], (0, i)))
heappush(self.heap, (self.height_map[last_row][i], (last_row, i)))

for i in range(self.rows_cnt):
last_col = self.cols_cnt - 1

heappush(self.heap, (self.height_map[i], (i, 0)))
heappush(self.heap, (self.height_map[i][last_col], (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
heappush(self.heap, (self.height_map[row][col], (row, col)))

def _get_min_bord(self):
min_bord = min(self.height_map + self.height_map[-1])
for row in self.height_map[1: -1]:
min_bord = min(min_bord, row, 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)
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))```

Topics: Python leetcode