欢迎光临
我们一直在努力

Kimi LeetCode LCP 31. 变换的迷宫 Rust实现

以下是 LCP 31. 变换的迷宫 的 Rust 实现,采用 BFS + 状态压缩 的思路,参考了 C++ 题解的状态设计 。

解题思路

状态设计:`f[time][x][y][scroll_state]` 表示在时刻 `time` 位于 `(x, y)`、卷轴使用状态为 `scroll_state` 时的最短步数。

状态值    含义    
0    未使用任何卷轴    
1    只使用了临时消除术    
2    只使用了永久消除术    
3    两个卷轴都使用了    

关键细节:
– 每时刻可以上下左右移动一步或停留原地(共 5 种选择)
– 临时消除术:仅让下一时刻的指定位置变为空地,用一次后消失
– 永久消除术:将指定位置永久变为空地,需要记录该位置坐标 `(px, py)`,后续所有时刻经过该位置都视为空地
– 只要在迷宫变化结束前(含最后时刻)到达终点 `(n-1, m-1)` 即算成功

Rust 代码

```rust
use std::collections::VecDeque;

struct Solution;

impl Solution {
    pub fn escape_maze(maze: Vec<Vec<String>>) -> bool {
        let layers = maze.len();
        if layers == 0 {
            return false;
        }
        let rows = maze[0].len();
        if rows == 0 {
            return false;
        }
        let cols = maze[0][0].len();

        // 五个移动方向:右、左、下、上、停留
        const DX: [i32; 5] = [0, 0, 1, -1, 0];
        const DY: [i32; 5] = [1, -1, 0, 0, 0];

        // 卷轴使用状态
        const NONE_USED: usize = 0;  // 未使用任何卷轴
        const ONLY_TEMP: usize = 1;  // 只使用了临时消除术
        const ONLY_PERM: usize = 2;  // 只使用了永久消除术
        const TEMP_PERM: usize = 3;  // 两个卷轴都使用了

        const INF: i32 = i32::MAX / 2;

        /// 状态值:记录到达该状态的步数和永久消除的位置
        #[derive(Clone, Copy)]
        struct StateVal {
            d: i32,   // 到达该状态的步数
            px: i32,  // 永久消除位置的 x 坐标(-1 表示没有使用永久消除术)
            py: i32,  // 永久消除位置的 y 坐标(-1 表示没有使用永久消除术)
        }

        // DP 数组:f[layer][row][col][scroll_state]
        let mut f: Vec<Vec<Vec<Vec<StateVal>>>> = vec![
            vec![
                vec![
                    vec![StateVal { d: INF, px: -1, py: -1 }; 4];
                    cols
                ];
                rows
            ];
            layers
        ];

        // BFS 队列,存储 (时刻, 行, 列, 卷轴状态)
        let mut q: VecDeque<(usize, usize, usize, usize)> = VecDeque::new();

        // 初始状态:时刻 0 在起点 (0, 0),未使用任何卷轴
        f[0][0][0][NONE_USED] = StateVal { d: 0, px: -1, py: -1 };
        q.push_back((0, 0, 0, NONE_USED));

        while let Some((c_layer, c_x, c_y, c_sc)) = q.pop_front() {
            let c_opt = f[c_layer][c_x][c_y][c_sc];
            let next_layer = c_layer + 1;

            // 下一时刻超出迷宫变化范围,无法继续移动
            if next_layer >= layers {
                continue;
            }

            for dir in 0..5 {
                let nx = c_x as i32 + DX[dir];
                let ny = c_y as i32 + DY[dir];

                // 边界检查
                if nx < 0 || nx >= rows as i32 || ny < 0 || ny >= cols as i32 {
                    continue;
                }
                let nx = nx as usize;
                let ny = ny as usize;

                // 检查下一时刻该位置是否是空地
                let next_is_empty = maze[next_layer][nx].as_bytes()[ny] == b'.';

                // 检查是否是永久消除的位置(永久消除后该位置始终可通行)
                let is_perm_pos = (c_sc == ONLY_PERM || c_sc == TEMP_PERM)
                    && c_opt.px == nx as i32
                    && c_opt.py == ny as i32;

                if next_is_empty || is_perm_pos {
                    // 该位置可通行,卷轴状态保持不变
                    if c_opt.d + 1 < f[next_layer][nx][ny][c_sc].d {
                        f[next_layer][nx][ny][c_sc] = StateVal {
                            d: c_opt.d + 1,
                            px: c_opt.px,
                            py: c_opt.py,
                        };
                        q.push_back((next_layer, nx, ny, c_sc));
                    }
                    continue;
                }

                // 下一时刻该位置是陷阱,考虑使用卷轴

                // 状态 1(只用临时):可以再用永久消除术 -> 状态 3
                if c_sc == ONLY_TEMP {
                    if c_opt.d + 1 < f[next_layer][nx][ny][TEMP_PERM].d {
                        f[next_layer][nx][ny][TEMP_PERM] = StateVal {
                            d: c_opt.d + 1,
                            px: nx as i32,  // 永久消除当前陷阱位置
                            py: ny as i32,
                        };
                        q.push_back((next_layer, nx, ny, TEMP_PERM));
                    }
                }

                // 状态 2(只用永久):可以再用临时消除术 -> 状态 3
                if c_sc == ONLY_PERM {
                    if c_opt.px == nx as i32 && c_opt.py == ny as i32 {
                        // 当前位置已被永久消除,直接通行(前面已处理,此处为保险)
                        if c_opt.d + 1 < f[next_layer][nx][ny][c_sc].d {
                            f[next_layer][nx][ny][c_sc] = StateVal {
                                d: c_opt.d + 1,
                                px: c_opt.px,
                                py: c_opt.py,
                            };
                            q.push_back((next_layer, nx, ny, c_sc));
                        }
                    } else {
                        // 使用临时消除术消除当前陷阱,进入状态 3
                        if c_opt.d + 1 < f[next_layer][nx][ny][TEMP_PERM].d {
                            f[next_layer][nx][ny][TEMP_PERM] = StateVal {
                                d: c_opt.d + 1,
                                px: c_opt.px,
                                py: c_opt.py,
                            };
                            q.push_back((next_layer, nx, ny, TEMP_PERM));
                        }
                    }
                }

                // 状态 0(都没用):可以使用临时或永久消除术
                if c_sc == NONE_USED {
                    // 使用永久消除术 -> 状态 2
                    if c_opt.d + 1 < f[next_layer][nx][ny][ONLY_PERM].d {
                        f[next_layer][nx][ny][ONLY_PERM] = StateVal {
                            d: c_opt.d + 1,
                            px: nx as i32,
                            py: ny as i32,
                        };
                        q.push_back((next_layer, nx, ny, ONLY_PERM));
                    }
                    // 使用临时消除术 -> 状态 1
                    if c_opt.d + 1 < f[next_layer][nx][ny][ONLY_TEMP].d {
                        f[next_layer][nx][ny][ONLY_TEMP] = StateVal {
                            d: c_opt.d + 1,
                            px: c_opt.px,
                            py: c_opt.py,
                        };
                        q.push_back((next_layer, nx, ny, ONLY_TEMP));
                    }
                }
            }
        }

        // 检查在任意时刻、任意卷轴状态下是否到达终点
        for layer in 0..layers {
            for sc in 0..4 {
                if f[layer][rows – 1][cols – 1][sc].d != INF {
                    return true;
                }
            }
        }

        false
    }
}
```

复杂度分析

– 时间复杂度:O(T \\times N \\times M \\times 4 \\times 5),其中 T 为时刻数,N \\times M 为迷宫大小,4 为卷轴状态数,5 为移动方向数
– 空间复杂度:O(T \\times N \\times M \\times 4),用于存储 DP 状态数组和 BFS 队列

下载文件:[lcp31_escape_maze.rs](sandbox:///mnt/agents/output/lcp31_escape_maze.rs)

 

赞(0)
未经允许不得转载:171主机测评 » Kimi LeetCode LCP 31. 变换的迷宫 Rust实现
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址