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

 

Topics: Python leetcode