跳至主要內容

寻找业务限制的尝试模型

mozzie大约 5 分钟算法算法

寻找业务限制的尝试模型

模型核心

这个模型的核心思想是:当我们在某个问题中遇到有业务限制的情境时,通常会有多个可行的操作(或状态转移),但在这些操作中只有某些特定的操作是可行的。通过限制条件(如走棋盘、路径的方向限制等),我们可以排除一些不可行的选择,从而优化搜索或状态转移过程。

常见问题类型

寻找业务限制的尝试模型通常适用于有约束的路径寻找问题有特定方向限制的状态转移问题。常见的应用场景包括:

  • 迷宫问题:例如在一个迷宫中从起点走到终点,但只能在某些方向上走。
  • 棋盘问题:例如棋盘上的骑士走法、皇后走法等。
  • 图的最短路径问题:在有权图或无权图中,求解从某一点到另一点的最短路径,路径可能会受到方向、障碍物等限制。

模型解析:迷宫问题(或路径问题)

假设有一个二维迷宫,我们从起点 S 出发,目标是找到到达终点 E 的一条路径。这个问题受到的业务限制是:从每个点只能走到相邻的几个方向(上下左右,或者有其他限制)。

  1. 状态定义
    • 定义一个二维数组 dp[i][j],表示从起点 S 到达位置 (i, j) 的最短路径长度。
  2. 状态转移
    • 对于每一个位置 (i, j),我们可以从它的四个相邻位置(上、下、左、右)中选择一个方向走,前提是该位置没有被障碍物阻挡,并且符合业务限制(如边界约束)。
    • dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i+1][j], dp[i][j+1]) + 1,表示从相邻位置选择一个到达当前格子的最短路径长度。
  3. 边界条件
    • 起点 S 位置的值初始化为0(即起点距离起点是0)。
    • 如果当前位置不可达(被墙壁或障碍物阻挡),则该位置的值为无穷大。
  4. 填表顺序
    • 边界条件:首先初始化起点,并且处理边界上的可达位置。
    • 递推过程:从起点开始,通过递推计算出每个位置的最短路径,遵循边界条件限制。

代码实现

public static int shortestPath(int[][] maze, int[] start, int[] end) {
    int m = maze.length;
    int n = maze[0].length;
    int[][] dp = new int[m][n];
    for (int[] row : dp) {
        Arrays.fill(row, Integer.MAX_VALUE); // 初始化所有位置为不可达
    }
    
    dp[start[0]][start[1]] = 0; // 起点的距离是0
    
    // 方向数组:上、下、左、右
    int[] dx = {-1, 1, 0, 0};
    int[] dy = {0, 0, -1, 1};
    
    // 使用队列进行广度优先搜索(BFS)
    Queue<int[]> queue = new LinkedList<>();
    queue.offer(start);
    
    while (!queue.isEmpty()) {
        int[] current = queue.poll();
        int x = current[0], y = current[1];
        
        // 遍历四个方向
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i], ny = y + dy[i];
            
            // 判断新位置是否在迷宫范围内,且不是障碍物
            if (nx >= 0 && nx < m && ny >= 0 && ny < n && maze[nx][ny] != 1 && dp[nx][ny] == Integer.MAX_VALUE) {
                dp[nx][ny] = dp[x][y] + 1; // 更新最短路径
                queue.offer(new int[] {nx, ny}); // 加入队列
            }
        }
    }
    
    return dp[end[0]][end[1]] == Integer.MAX_VALUE ? -1 : dp[end[0]][end[1]]; // 如果不可达返回-1
}

状态转移过程

  1. 边界条件:
    • 起点 dp[start[0]][start[1]] 初始化为0,表示从起点出发。
    • 每个不可达位置(如墙壁)初始化为 Integer.MAX_VALUE,表示该位置不能到达。
  2. 递推过程:
    • 从起点开始,逐步计算到达每个位置的最短路径。每次计算时,从当前位置可以选择四个方向(上、下、左、右)中的一个,但必须遵循业务限制:即不走到墙壁或者出界的地方。

复杂度分析

  • 时间复杂度:O(m * n),其中 mn 分别是迷宫的行数和列数。每个位置都最多会被访问一次。
  • 空间复杂度:O(m * n),用于存储动态规划表和队列。

模型解析:棋盘问题(如骑士问题)

假设一个 8x8 的棋盘上,我们要求解从某个位置出发的骑士的最短路径问题,目标是找到到达目标位置的最短路径。骑士的走法受到业务限制——只能按照 "L" 形的跳跃方式走(上、下、左、右的组合)。

  1. 状态定义

    • 定义一个二维数组 dp[i][j],表示从起点位置到 (i, j) 位置的最短路径长度。
  2. 状态转移

    • 骑士的跳跃方式有8种,分别为:

      (+2, +1), (+2, -1), (-2, +1), (-2, -1)
      (+1, +2), (+1, -2), (-1, +2), (-1, -2)
      
    • 对于每个位置 (i, j),我们可以从它的8个方向中选择一个方向跳跃到下一个位置。

  3. 边界条件

    • 起点位置的 dp 值初始化为0。
    • 如果位置超出棋盘范围,或者被阻挡,则不能跳跃。
  4. 填表顺序

    • 边界条件:初始化起点,处理边界上的可达位置。
    • 递推过程:从起点出发,通过递推计算出每个位置的最短路径。

代码实现

public static int knightShortestPath(int N, int[] start, int[] end) {
    int[][] dp = new int[N][N];
    for (int[] row : dp) {
        Arrays.fill(row, Integer.MAX_VALUE);
    }

    dp[start[0]][start[1]] = 0;

    int[] dx = {2, 2, -2, -2, 1, 1, -1, -1};
    int[] dy = {1, -1, 1, -1, 2, -2, 2, -2};

    Queue<int[]> queue = new LinkedList<>();
    queue.offer(start);

    while (!queue.isEmpty()) {
        int[] current = queue.poll();
        int x = current[0], y = current[1];

        for (int i = 0; i < 8; i++) {
            int nx = x + dx[i], ny = y + dy[i];

            if (nx >= 0 && nx < N && ny >= 0 && ny < N && dp[nx][ny] == Integer.MAX_VALUE) {
                dp[nx][ny] = dp[x][y] + 1;
                queue.offer(new int[] {nx, ny});
            }
        }
    }

    return dp[end[0]][end[1]] == Integer.MAX_VALUE ? -1 : dp[end[0]][end[1]];
}

复杂度分析

  • 时间复杂度:O(N^2),每个位置会被访问一次,因此时间复杂度是棋盘大小的平方。
  • 空间复杂度:O(N^2),用于存储棋盘上的状态信息。

总结

寻找业务限制的尝试模型适用于那些在状态转移或路径选择中有特定约束的问题。通过合理设置边界条件限制方向可行操作,我们可以优化计算过程,避免无效的状态转移。常见的应用场景包括迷宫路径问题棋盘问题图的最短路径问题等。

贡献者: mozzie