题目链接:网格中的最短路径
知识点:「数据分治」、「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);
}