Medium

题目描述

给定一个 m x n 的二进制网格 grid,其中:

  • grid[i][j] == 0 表示空格
  • grid[i][j] == 1 表示镜子

一个机器人从网格的左上角 (0, 0) 开始,想要到达右下角 (m - 1, n - 1)。它只能向右或向下移动。如果机器人尝试移动到一个镜子格子,它会在进入该格子之前被反射:

  • 如果它尝试向右移动到一个镜子,它会被转向下方并移动到镜子正下方的格子
  • 如果它尝试向下移动到一个镜子,它会被转向右方并移动到镜子正右方的格子

如果这种反射会导致机器人移动到网格边界之外,该路径被认为是无效的,不应计入结果。

返回从 (0, 0) 到 (m - 1, n - 1) 的唯一有效路径数量。

由于答案可能很大,请返回结果对 10^9 + 7 取模的值。

注意:如果反射将机器人移动到另一个镜子格子,机器人会根据进入该镜子的方向立即再次反射:如果它是向右进入的,会被转向下方;如果它是向下进入的,会被转向右方。这个过程会继续,直到到达最后一个格子、机器人移出边界或机器人移动到非镜子格子。

示例 1:

输入:grid = [[0,1,0],[0,0,1],[1,0,0]]
输出:5

示例 2:

输入:grid = [[0,0],[0,0]]
输出:2

示例 3:

输入:grid = [[0,1,1],[1,1,0]]
输出:1

约束:

  • m == grid.length
  • n == grid[i].length
  • 2 <= m, n <= 500
  • grid[i][j] 是 0 或 1
  • grid[0][0] == grid[m - 1][n - 1] == 0

解题思路

这是一道动态规划题目,关键在于处理镜子的反射机制。

核心思路:

  1. 预计算跳跃目标:对于每个位置 (i,j) 和每个移动方向(右/下),预计算如果下一步遇到镜子时实际会到达的位置。这样可以避免在DP过程中重复计算反射链。

  2. 动态规划状态定义dp[i][j] 表示到达位置 (i,j) 的路径数量。

  3. 状态转移

    • 初始化:dp[0][0] = 1
    • 对于每个位置 (i,j),如果 dp[i][j] > 0,则将其贡献传递给两个可能的下一步位置(通过预计算的跳跃表)
  4. 反射处理

    • 向右遇到镜子:转向下方
    • 向下遇到镜子:转向右方
    • 连续反射直到到达非镜子格子或出界

算法步骤:

  1. 预计算每个位置每个方向的实际到达位置
  2. 使用动态规划计算路径数
  3. 对所有运算结果取模

这种方法将复杂的反射逻辑预处理,使得DP过程变得简洁高效。

代码实现

class Solution {
public:
    int uniquePaths(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        const int MOD = 1e9 + 7;
        
        // go[i][j][0] = 从(i,j)向右移动的实际到达位置
        // go[i][j][1] = 从(i,j)向下移动的实际到达位置
        vector<vector<vector<pair<int,int>>>> go(m, vector<vector<pair<int,int>>>(n, vector<pair<int,int>>(2, {-1, -1})));
        
        // 预计算每个位置每个方向的跳跃目标
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 向右移动
                if (j + 1 < n) {
                    int x = i, y = j + 1;
                    int dir = 0; // 0=right, 1=down
                    while (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) {
                        if (dir == 0) { // 向右遇到镜子,转向下
                            x++;
                            dir = 1;
                        } else { // 向下遇到镜子,转向右
                            y++;
                            dir = 0;
                        }
                    }
                    if (x >= 0 && x < m && y >= 0 && y < n) {
                        go[i][j][0] = {x, y};
                    }
                }
                
                // 向下移动
                if (i + 1 < m) {
                    int x = i + 1, y = j;
                    int dir = 1; // 0=right, 1=down
                    while (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) {
                        if (dir == 0) { // 向右遇到镜子,转向下
                            x++;
                            dir = 1;
                        } else { // 向下遇到镜子,转向右
                            y++;
                            dir = 0;
                        }
                    }
                    if (x >= 0 && x < m && y >= 0 && y < n) {
                        go[i][j][1] = {x, y};
                    }
                }
            }
        }
        
        // 动态规划
        vector<vector<long long>> dp(m, vector<long long>(n, 0));
        dp[0][0] = 1;
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dp[i][j] > 0) {
                    // 向右移动
                    if (go[i][j][0].first != -1) {
                        int x = go[i][j][0].first, y = go[i][j][0].second;
                        dp[x][y] = (dp[x][y] + dp[i][j]) % MOD;
                    }
                    
                    // 向下移动
                    if (go[i][j][1].first != -1) {
                        int x = go[i][j][1].first, y = go[i][j][1].second;
                        dp[x][y] = (dp[x][y] + dp[i][j]) % MOD;
                    }
                }
            }
        }
        
        return dp[m-1][n-1];
    }
};
class Solution:
    def uniquePaths(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        MOD = 10**9 + 7
        
        # go[i][j][0] = 从(i,j)向右移动的实际到达位置
        # go[i][j][1] = 从(i,j)向下移动的实际到达位置
        go = [[[-1, -1] for _ in range(2)] for _ in range(n)] for _ in range(m)]
        
        # 预计算每个位置每个方向的跳跃目标
        for i in range(m):
            for j in range(n):
                # 向右移动
                if j + 1 < n:
                    x, y = i, j + 1
                    direction = 0  # 0=right, 1=down
                    while 0 <= x < m and 0 <= y < n and grid[x][y] == 1:
                        if direction == 0:  # 向右遇到镜子,转向下
                            x += 1
                            direction = 1
                        else:  # 向下遇到镜子,转向右
                            y += 1
                            direction = 0
                    if 0 <= x < m and 0 <= y < n:
                        go[i][j][0] = [x, y]
                
                # 向下移动
                if i + 1 < m:
                    x, y = i + 1, j
                    direction = 1  # 0=right, 1=down
                    while 0 <= x < m and 0 <= y < n and grid[x][y] == 1:
                        if direction == 0:  # 向右遇到镜子,转向下
                            x += 1
                            direction = 1
                        else:  # 向下遇到镜子,转向右
                            y += 1
                            direction = 0
                    if 0 <= x < m and 0 <= y < n:
                        go[i][j][1] = [x, y]
        
        # 动态规划
        dp = [[0] * n for _ in range(m)]
        dp[0][0] = 1
        
        for i in range(m):
            for j in range(n):
                if dp[i][j] > 0:
                    # 向右移动
                    if go[i][j][0][0] != -1:
                        x, y = go[i][j][0]
                        dp[x][y] = (dp[x][y] + dp[i][j]) % MOD
                    
                    # 向下移动
                    if go[i][j][1][0] != -1:
                        x, y = go[i][j][1]
                        dp[x][y] = (dp[x][y] + dp[i][j]) % MOD
        
        return dp[m-1][n-1]
public class Solution {
    public int UniquePaths(int[][] grid) {
        int m = grid.Length, n = grid[0].Length;
        const int MOD = 1000000007;
        
        // go[i,j,0] = 从(i,j)向右移动的实际到达位置
        // go[i,j,1] = 从(i,j)向下移动的实际到达位置
        int[,,] go = new int[m, n, 4]; // [x, y] for each direction
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                go[i, j, 0] = go[i, j, 2] = -1; // 初始化为-1表示无效
            }
        }
        
        // 预计算每个位置每个方向的跳跃目标
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 向右移动
                if (j + 1 < n) {
                    int x = i, y = j + 1;
                    int dir = 0; // 0=right, 1=down
                    while (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) {
                        if (dir == 0) { // 向右遇到镜子,转向下
                            x++;
                            dir = 1;
                        } else { // 向下遇到镜子,转向右
                            y++;
                            dir = 0;
                        }
                    }
                    if (x >= 0 && x < m && y >= 0 && y < n) {
                        go[i, j, 0] = x;
                        go[i, j, 1] = y;
                    }
                }
                
                // 向下移动
                if (i + 1 < m) {
                    int x = i + 1, y = j;
                    int dir = 1; // 0=right, 1=down
                    while (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) {
                        if (dir == 0) { // 向右遇到镜子,转向下
                            x++;
                            dir = 1;
                        } else { // 向下遇到镜子,转向右
                            y++;
                            dir = 0;
                        }
                    }
                    if (x >= 0 && x < m && y >= 0 && y < n) {
                        go[i, j, 2] = x;
                        go[i, j, 3] = y;
                    }
                }
            }
        }
        
        // 动态规划
        long[,] dp = new long[m, n];
        dp[0, 0] = 1;
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dp[i, j] > 0) {
                    // 向右移动
                    if (go[i, j, 0] != -1) {
                        int x = go[i, j, 0], y = go[i, j, 1];
                        dp[x, y] = (dp[x, y] + dp[i, j]) % MOD;
                    }
                    
                    // 向下移动
                    if (go[i, j, 2] != -1) {
                        int x = go[i, j, 2], y = go[i, j, 3];
                        dp[x, y] = (dp[x, y] + dp[i, j]) % MOD;
                    }
                }
            }
        }
        
        return (int)dp[m-1, n-1];
    }
}
var uniquePaths = function(grid) {
    const MOD = 1000000007;
    const m = grid.length;
    const n = grid[0].length;
    
    const memo = new Map();
    
    function getKey(r, c, dir) {
        return `${r},${c},${dir}`;
    }
    
    function dfs(r, c, dir) {
        if (r < 0 || r >= m || c < 0 || c >= n) {
            return 0;
        }
        
        if (r === m - 1 && c === n - 1) {
            return 1;
        }
        
        const key = getKey(r, c, dir);
        if (memo.has(key)) {
            return memo.get(key);
        }
        
        let result = 0;
        
        if (grid[r][c] === 1) {
            if (dir === 0) {
                result = dfs(r + 1, c, 1);
            } else {
                result = dfs(r, c + 1, 0);
            }
        } else {
            result = (dfs(r, c + 1, 0) + dfs(r + 1, c, 1)) % MOD;
        }
        
        memo.set(key, result);
        return result;
    }
    
    return dfs(0, 0, -1);
};

复杂度分析

指标复杂度
时间-
空间-