【搜索】A060_LC_網格中的最短路徑(裸 bfs(×) / PQ(×) / 三維狀態記錄)

一、Problem

Given a m * n grid, where each cell is either 0 (empty) or 1 (obstacle). In one step, you can move up, down, left or right from and to an empty cell.

Return the minimum number of steps to walk from the upper left corner (0, 0) to the lower right corner (m-1, n-1) given that you can eliminate at most k obstacles. If it is not possible to find such walk return -1.

Input: 
grid = 
[[0,0,0],
 [1,1,0],
 [0,0,0],
 [0,1,1],
 [0,0,0]], 
k = 1
Output: 6
Explanation: 
The shortest path without eliminating any obstacle is 10. 
The shortest path with one obstacle elimination at position (3,2) is 6. Such path is (0,0) -> (0,1) -> (0,2) -> (1,2) -> (2,2) -> (3,2) -> (4,2).

Constraints:

grid.length == m
grid[0].length == n
1 <= m, n <= 40
1 <= k <= m*n
grid[i][j] == 0 or 1
grid[0][0] == grid[m-1][n-1] == 0

二、Solution

方法一:裸 bfs(WA)

並不是沒遇到一個障礙物就去消除它,因爲有些障礙物是不值得我們去消除的。

class Solution {
	int b, R, C, g[][];
	boolean[][] vis;
	final static int[][] d = { {1,0},{0,-1},{0,1},{-1,0} };

	int bfs(int sx, int sy) {
		Queue<Pos> q = new ArrayDeque<>();
		q.add(new Pos(sx, sy, 0));
		vis[sx][sy]  = true;

		while (!q.isEmpty()) {
			Pos t = q.poll();
			if (t.x == R-1 && t.y == C-1)
				return t.step;
            
			for (int k = 0; k < 4; k++) {
				int tx = t.x + d[k][0], ty = t.y + d[k][1];
				if (tx < 0 || tx >= R || ty < 0 || ty >= C || vis[tx][ty])
					continue;
                if (g[tx][ty] == 1) {
                    if (b-- > 0) {
						q.add(new Pos(tx, ty, t.step+1));
						vis[tx][ty] = true;
					} else {
						continue;
					}
				} else {
					q.add(new Pos(tx, ty, t.step+1));
					vis[tx][ty] = true;
				}
			}
		}
		return -1;
	}
    public int shortestPath(int[][] grid, int k) {
    	this.b = k;
    	this.g = grid;
    	R = g.length;
    	C = g[0].length;
    	vis = new boolean[R][C];
    	return bfs(0, 0);
    }
    class Pos {
	    int x, y, step;
	    public Pos(int _x, int _y, int _step) {
	       x = _x;
	       y = _y;
	       step = _step;
	    }
	}
}

以上做法引以爲戒…,但我也嘗試過用優先級隊列做:先走消耗次數 k 少了點,但還是 WA…

class Solution {
	int K, R, C, g[][];
	boolean[][] vis;
	final static int[][] d = { {1,0},{0,-1},{0,1},{-1,0} };

	int bfs(int sx, int sy) {
		Queue<Pos> q = new PriorityQueue<>((e1, e2) -> {
			if (e1.step == e2.step)
				return e2.k - e1.k;
			return e1.step - e2.step;
		});
		q.add(new Pos(sx, sy, 0, 0));
		vis[sx][sy]  = true;
        
		while (!q.isEmpty()) {
			Pos t = q.poll();
			if (t.x == R-1 && t.y == C-1)
				return t.step;
			for (int j = 0; j < 4; j++) {
				int tx = t.x + d[j][0], ty = t.y + d[j][1];
				if (tx < 0 || tx >= R || ty < 0 || ty >= C || vis[tx][ty])
					continue;
                if (g[tx][ty] == 1 && t.k+1 <= K) {
                    q.add(new Pos(tx, ty, t.step+1, t.k+1));
                    vis[tx][ty] = true;
				} else if (g[tx][ty] == 0){
					q.add(new Pos(tx, ty, t.step+1, t.k));
					vis[tx][ty] = true;
				}
			}
		}
		return -1;
	}
    public int shortestPath(int[][] grid, int k) {
    	this.g = grid;
    	K = k;
    	R = g.length;
    	C = g[0].length;
    	vis = new boolean[R][C];
    	return bfs(0, 0);
    }
    class Pos {
	    int x, y, step, k;
	    public Pos(int _x, int _y, int _step, int k) {
	       x = _x;
	       y = _y;
	       step = _step;
	       this.k = k;
	    }
	}
}

但爲什麼先走消耗 k 少的點不行?服了…


方法二:記錄狀態 k

好吧,我認了…別人都是用 vis[x][y][k]vis[x][y][k] 表示某一個點的某一個狀態是否被訪問過,也就是說如果我之前來過這個點,如果從某個點還走到這個點且狀態相同,那麼這個點不需要再走了…

class Solution {
	int K, R, C, g[][];
	boolean[][][] vis;
	final static int[][] d = { {1,0},{0,-1},{0,1},{-1,0} };

	int bfs(int sx, int sy) {
		Queue<Pos> q = new ArrayDeque<>();
		q.add(new Pos(sx, sy, 0, 0));
		vis[sx][sy][0]  = true;
        
		while (!q.isEmpty()) {
			Pos t = q.poll();
			if (t.x == R-1 && t.y == C-1)
				return t.step;
			for (int j = 0; j < 4; j++) {
				int tx = t.x + d[j][0], ty = t.y + d[j][1];
				if (tx < 0 || tx >= R || ty < 0 || ty >= C || vis[tx][ty][t.k])
					continue;
                if (g[tx][ty] == 1 && t.k+1 <= K && !vis[tx][ty][t.k+1]) {
                    q.add(new Pos(tx, ty, t.step+1, t.k+1));
                    vis[tx][ty][t.k+1] = true;
				} else if (g[tx][ty] == 0){
					q.add(new Pos(tx, ty, t.step+1, t.k));
					vis[tx][ty][t.k] = true;
				}
			}
		}
		return -1;
	}
    public int shortestPath(int[][] grid, int k) {
    	this.g = grid;
    	K = k;
    	R = g.length;
    	C = g[0].length;
    	vis = new boolean[R][C][k+1];
    	return bfs(0, 0);
    }
    class Pos {
	    int x, y, step, k;
	    public Pos(int _x, int _y, int _step, int k) {
	       x = _x;
	       y = _y;
	       step = _step;
	       this.k = k;
	    }
	}
}

複雜度分析

  • 時間複雜度:O(R×C×min(k,R×C))O(R × C × min(k, R × C))
  • 空間複雜度:O(R×C×k)O(R × C × k)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章