Different Paths III -- Different paths III

980. Different paths III - LeetCode icon-default.png?t=N6B9https://leetcode.cn/problems/unique-paths-iii/description/

On a two-dimensional grid, there are four types of squares:

1 indicates the starting square. And there's only one starting grid.
2 indicates the end square, and there is only one end square.
0 represents the empty squares we can walk through.
-1 is an obstacle we can't cross.
Returns the number of different paths from the start square to the end square when walking in four directions (up, down, left, and right).

Each barrier-free square must be passed once, but the same square cannot be passed repeatedly in a path.

On a two-dimensional grid  grid , there are 4 types of squares:

  • 1 Indicates the starting square. And there is only one starting square.
  • 2 Indicates the end square, and there is only one end square.
  • 0 Represents an empty square that we can walk through.
  • -1 Represents an obstacle that we cannot overcome.

Returns the number of different paths from the starting square to the ending square when walking in four directions (up, down, left, right) .

Each barrier-free square must be passed once, but the same square cannot be passed twice in a path .

Example 1:

Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
 Output: 2Explanation
 : We have the following two paths: 
1. (0 ,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0 ),(2,1),(2,2) 
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1) ,(0,2),(0,3),(1,3),(1,2),(2,2)

Example 2:

Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
 Output: 4
 Explanation: We have the following four paths: 
1. (0,0 ),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0), (2,1),(2,2),(2,3) 
2. (0,0),(0,1),(1,1),(1,0),(2,0),( 2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) 3. 
(0,0),(1 ,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3 ),(1,3),(2,3) 
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1) ,(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)

Example 3:

Input: [[0,1],[2,0]]
 Output: 0
 Explanation: 
No path can completely pass through every empty square once. 
Note that the start and end squares can be anywhere in the grid.

hint:

  • 1 <= grid.length * grid[0].length <= 20

Method 1: Backtrack

If there are n zeros in the matrix, then a qualified path is a path of length (n+1) that starts with 1 and ends with 2, does not pass through −1, and passes through each point only once. To find out all the qualified paths, we can use the backtracking method to define the function dfs, which represents the number of paths starting from the point (i,j) and going through n points to the end in the current grid state. When a point is reached, if the current point is the end point and (n+1) points have been passed, then a qualified path is formed, otherwise it is not formed. If the current point is not the end point, the current point is marked as −1, indicating that the path cannot pass this point in the future, and then continue to expand in four directions at this point, if it does not exceed the boundary and the value of the next point is 0 or 2, it means that the path can continue to expand. After exploring the four directions, you need to change the value of the current point to the original value. The sum of the qualified paths in the four directions is the number of qualified paths in the current state. The final return is the number of paths the grid needs to pass through (n+1) points from the starting point in its initial state.

Method 1: Backtracking

Idea: According to the requirements, assuming there are n zeros in the matrix, then a qualified path is a path with length (n+1), starting from 1, ending at 2, not passing through −1, and passing each point only once. path. To find out all qualified paths, you can use the backtracking method and define the function dfs, which represents the number of paths starting from point (i, j) and going through n points to the end point in the current grid state. When reaching a point, if the current point is the end point and has passed (n+1) points, then a qualified path is formed, otherwise it is not formed. If the current point is not the end point, mark the current point as −1, which means that this path cannot pass through this point in the future, and then continue to expand in four directions at this point. If it does not exceed the boundary and the value of the next point If it is 0 or 2, it means that this path can continue to expand. After detecting the four directions, you need to change the current point value to the original value. The sum of qualified paths in the four directions is the number of qualified paths in the current state. What ultimately needs to be returned is the number of paths that the grid needs to pass through (n+1) points starting from the starting point in its initial state.


class Solution {
    static int[][] dirs = {
   
   {-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    public int uniquePathsIII(int[][] grid) {
        int r = grid.length, c = grid[0].length;
        int si = 0, sj = 0, n = 0;
        
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (grid[i][j] == 0) {
                    n++;
                } else if (grid[i][j] == 1) {
                    n++;
                    si = i;
                    sj = j;
                }
            }
        }
        return dfs(grid, si, sj, n);
    }

    public int dfs(int[][] grid, int i, int j, int n) {
        if (grid[i][j] == 2) {
            return n == 0 ? 1 : 0;
        }
        int r = grid.length, c = grid[0].length;
        int t = grid[i][j];
        grid[i][j] = -1;
        int res = 0;
        for (int[] dir : dirs) {
            int ni = i + dir[0], nj = j + dir[1];
            if (ni >= 0 && ni < r && nj >= 0 && nj < c && (grid[ni][nj] == 0 || grid[ni][nj] == 2)) {
                res += dfs(grid, ni, nj, n - 1);
            }
        }
        grid[i][j] = t;
        return res;
    }
}

 

Method 2: Memorized search + state compression

Idea: Method 1 of the backtracking function, even in the case of i,j,n are the same, the return value of the function will be different, because the path through the point is different, resulting in the current situation grid\textit{grid}grid state is also different. Therefore, we can put the state of the grid into the input parameters of the function, thereby reducing the time complexity by memorizing the search.

A binary number st is used to represent the point that the path has not passed through (the initial state is all the points with a value of 0 and the end point), and the coordinates of the point need to correspond to the bits of the binary number. Define the function dp, the input parameters are the current coordinate i,j and the binary set st of the points passed through, and the return value is the number of paths starting from the point (i,j), passing through the set of points represented by st, and finally reaching the end point. If the current point is the end point and there are no unpassed points left, then the current return value is 1, otherwise 0. If the current point is not the end point, you need to explore the four directions, if the next point is within the boundary and has not yet passed (judged by the bit-sum operation), you need to go to that point and remove the coordinates of that point from the set of unpassed points (determined by the bit-sum operation). The sum of the qualified paths in the four directions is the number of qualified paths in the current state. The final thing that needs to be returned is the number of paths from the starting point for the set of points passed to all points with a value of 0 and the end point. A memorized search is used during the function call, and each state is calculated at most once.

Method 2: Memorized search + state compression
idea: Method 1’s backtracking function, even when i, j, n are the same, the return value of the function will be different because the paths pass through different points, resulting in the grid in the current situation The status of \textit{grid}grid is also different. Therefore, we can put the grid's state into the input parameters of the function and use memoized search to reduce time complexity.

A binary number st is used to represent the points that the path has not passed (in the initial state, all points with a value of 0 and the end point). The coordinates of the points need to correspond to the bits of the binary number one-to-one. Define the function dp. The input parameters are the current coordinates i, j and the binary set st of the passing points. The return value is the path starting from the point (i, j), passing through the set of points represented by st, and finally reaching the end point. number. If the current point is the end point and there are no unpassed points left, then the current return value is 1, otherwise it is 0. If the current point is not the end point, you need to explore four directions. If the next point is within the boundary and has not been passed yet (judged by bitwise sum operation), you need to go to that point and change the coordinates of that point from never Remove from the set of passing points (using bitwise XOR operation). The sum of qualified paths in the four directions is the number of qualified paths in the current state. What ultimately needs to be returned is the number of paths starting from the starting point, the set of passing points, all points with a value of 0 and the end point. Memorized search is used during the function call, and each state will only be calculated once at most.

class Solution {
    static int[][] dirs = {
   
   {-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    Map<Integer, Integer> memo = new HashMap<Integer, Integer>();

    public int uniquePathsIII(int[][] grid) {
        int r = grid.length, c = grid[0].length;
        int si = 0, sj = 0, st = 0;
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (grid[i][j] == 0 || grid[i][j] == 2) {
                    st |= 1 << (i * c + j);
                } else if (grid[i][j] == 1) {
                    si = i;
                    sj = j;
                }
            }
        }
        return dp(grid, si, sj, st);
    }

    public int dp(int[][] grid, int i, int j, int st) {
        if (grid[i][j] == 2) {
            return st == 0 ? 1 : 0;
        }
        int r = grid.length, c = grid[0].length;
        int key = ((i * c + j) << (r * c)) + st;
        if (!memo.containsKey(key)) {
            int res = 0;
            for (int[] dir : dirs) {
                int ni = i + dir[0], nj = j + dir[1];
                if (ni >= 0 && ni < r && nj >= 0 && nj < c && (st & (1 << (ni * c + nj))) > 0) {
                    res += dp(grid, ni, nj, st ^ (1 << (ni * c + nj)));
                }
            }
            memo.put(key, res);
        }
        return memo.get(key);
    }
}

 

Guess you like

Origin blog.csdn.net/m0_72572822/article/details/132097187