「每日 Rust」数据分治,但是用 as 来解决

117 阅读3分钟

题目链接:网格中的最短路径

知识点:「数据分治」、「as」

题目讲解

哦,让我们看一下今天的每日一题,是一个找路径的题!

题目告诉我们,我们要从左上角的格子走到右下角,一路上有很多的障碍,我们可以移除掉其中 k 个,问我们在这样的能力下我们最少需要多少步。

我说这不是轻而易举,题目难度不大,作为一个竞赛生,我们直接设立状态 v[x][y][t] 表示走到 (x, y) 还可以移除 t 个障碍,然后直接左上角搜到右下角,一个简单的 BFS 就能解决。

让我们一起算一下时间复杂度,地图大小是 40 乘 40,k 最大是 1600,最坏情况下蛇形移动可能需要 1600 步(其实不用这么多),那么最坏情况下的时间复杂度就是 40*40*1600*1600=4096000000,哈哈,1e9 的范围,做不了。

那该怎么办呢?在稍加思索后,经过简单的分析,我说这不是轻而易举,题目难度不大,作为一个竞赛生,我们可以很显然地发现,如果 k 的值太大了,我们就可以横冲直撞、蛮不讲理、肆意妄为、直奔终点,具体来说如果 k >= n + m - 2 答案其实就已经固定了,就是 n + m - 2,那么我们之前的做法就没有问题了!

算一下 40*40*80*1600=204800000,正正好最坏的情况下一秒钟能够运行完没有问题。接下来我们要做的就是常规的 BFS,每次取出节点往四个方向判断能不能走,走了会不会让答案变得更优,如果到了终点就直接返回,不行就返回 -1。

那我们开写!

代码分析

重头戏来了,校招题算法往往不是难点,怎么写出来才是问题。我们尝试用 Rust 来写掉这道题。

今天主要一个知识点:as,帮助我们灵活地转换用作数组下标的类型 usize 和进行加减运算的类型 i32,这样我们就可以很方便地写出广搜。

具体用法很简单,如果我们想让某一个 usize 类型的变量 a 临时当作 i32 来使用,我们不需要额外定义变量来储存,可以直接 a as i32,如果是想要指定某个类型的数字的话,直接贴着写就行,如 0usize 或者 1i32

这样我们就可以很方便地转换这两个类型了,但是需要注意的一点是,这样子的转换可能会造成 panic,在 i32 转 usize 之前,我们一定要判断负数的情况,在做加减运算的时候最好全部都转成 i32 来运算。

考虑好这个地方,其他的正常写就行,用 Rust 也没有什么特殊的地方。

有些地方可以注意一下,比如定义多维数组的方式,会更加类似于 Go 类语言中的逆向定义,比如定义一个 n 行 m 列的数组,使用的时候是 a[n][m],但是定义的时候需要反过来,let a = [[0; m]; n]

示例代码

struct Solution;

impl Solution {
    pub fn shortest_path(grid: Vec<Vec<i32>>, k: i32) -> i32 {
        const N: usize = 41;
        let n = grid.len() as i32;
        let m = grid[0].len() as i32;

        if k >= n + m - 2 {
            return n + m - 2;
        }

        let dire = [[-1i32, 0], [0, -1], [1, 0], [0, 1]];
        let mut q = Vec::new();
        let mut mark = [[[-1; N * 2]; N]; N];

        q.push((0usize, 0usize, k as usize));
        mark[0][0][k as usize] = 0;

        let mut head = 0;
        let mut tail = 1;

        while head < tail {
            let t = q[head];
            head += 1;
            for i in 0..4 {
                let mut nxt = (t.0 as i32 + dire[i][0], t.1 as i32 + dire[i][1], t.2 as i32);
                if nxt.0 < 0 || nxt.1 < 0 || nxt.0 >= n || nxt.1 >= m {
                    continue;
                }
                if grid[nxt.0 as usize][nxt.1 as usize] == 1 {
                    nxt.2 -= 1;
                }
                if nxt.2 < 0 {
                    continue;
                }
                let nxt = (nxt.0 as usize, nxt.1 as usize, nxt.2 as usize);
                let nxtv = mark[t.0][t.1][t.2] + 1;
                let havev = mark[nxt.0][nxt.1][nxt.2];
                if havev != -1 && nxtv >= havev {
                    continue;
                }
                if nxt.0 == (n - 1) as usize && nxt.1 == (m - 1) as usize {
                    return nxtv;
                }
                mark[nxt.0][nxt.1][nxt.2] = nxtv;
                q.push(nxt);
                tail += 1;
            }
        }
        -1
    }
}

fn main() {
    let grid = vec![
        vec![0, 0, 0],
        vec![1, 1, 0],
        vec![0, 0, 0],
        vec![0, 1, 1],
        vec![0, 0, 0],
    ];
    let k = 1;
    let res = Solution::shortest_path(grid, k);
    println!("Res: {}", res);
}