周賽的時候的題目:每次周賽基本上第四道題都做不出來,除了感慨自己菜以外,還會感嘆那些做的很快的大佬,是真的很厲害了!!!
題目描述:
給你一個 m * n 的網格,其中每個單元格不是 0(空)就是 1(障礙物)。每一步,您都可以在空白單元格中上、下、左、右移動。
如果您 最多 可以消除 k 個障礙物,請找出從左上角 (0, 0) 到右下角 (m-1, n-1) 的最短路徑,並返回通過該路徑所需的步數。如果找不到這樣的路徑,則返回 -1。
示例 1:
輸入:
grid =
[[0,0,0],
[1,1,0],
[0,0,0],
[0,1,1],
[0,0,0]],
k = 1
輸出:6
解釋:
不消除任何障礙的最短路徑是 10。
消除位置 (3,2) 處的障礙後,最短路徑是 6 。該路徑是 (0,0) -> (0,1) -> (0,2) -> (1,2) -> (2,2) -> (3,2) -> (4,2).
示例 2:
輸入:
grid =
[[0,1,1],
[1,1,1],
[1,0,0]],
k = 1
輸出:-1
解釋:
我們至少需要消除兩個障礙才能找到這樣的路徑。
分析:
首先這題我開始的思路是這樣,記錄每個位置不消除障礙物的最短路徑,消除一個障礙物的最短路徑,消除兩個障礙物的最短路徑…消除k個障礙物的最短路徑,所以需要構造 dp[row] [col] [k] 來記錄,看這個結構就知道,這是一個動態規劃的問題,但是問題是,怎麼訪問各個點的最短路徑,從上到下?從左到右?我很蒙。
方法:動態規劃+DBS
用一個隊列(爲啥不用棧,到最後說明),首先放入的是dp[0][0][0]的值爲0,之後更新上下左右dp[gx][gy][k],但是注意!!!
首先,只有當dp[gx][gy][k]爲-1的時候才需要更新,因爲如果已經有值一定是之前更新了的,那麼之前更新的步數應該是比當前更新的步數要小的。
第二個問題,當更新的值位置的值:
當前位置爲grid[gx][gy] == 0時,那麼k = a.k(就是從隊列出來的k值)
當前位置爲grid[gx][gy] == 1時,那麼k= a.k+1,因爲你當前爲1要想到達當前位置,必須消除當前的障礙!!!
之後入隊。
這裏說明一個問題:這裏存在着一個循環,就是你更新下面一個結點(這個結點grid[i][j] = 1)之後,入隊,取出,你又更新上面的值,看起來好像是循環了,但是用一個條件,就是你的k是有限的!!!這樣只會更新到k,超過k就不做更新,就不會入隊循環了!!!
代碼如下:
struct node{
int x,y,k;
node() {}
node(int _x,int _y,int _k) { x=_x; y=_y; k=_k; }
};
class Solution {
public:
int shortestPath(vector<vector<int>>& grid, int k) {
queue<node> q;
int row=grid.size();
int col=grid[0].size();
int x[4] = {0,0,1,-1};
int y[4] = {1,-1,0,0};
int dp[row][col][k+1];
memset(dp, -1, sizeof(dp));
dp[0][0][0] = 0;
q.push(node(0,0,0));
while(!q.empty()){
node a = q.front();
q.pop();
for(int i = 0;i < 4;i++){
int gx = a.x+x[i];
int gy = a.y+y[i];
if(gx >= 0&&gx < row&&gy >= 0&&gy < col&&a.k+grid[gx][gy] <= k){
if(dp[gx][gy][a.k+grid[gx][gy]] == -1){
dp[gx][gy][a.k+grid[gx][gy]] = dp[a.x][a.y][a.k]+1;
q.push(node(gx,gy,a.k+grid[gx][gy]));
}
}
}
}
int ans = dp[row-1][col-1][0];
for(int i = 1;i <= k;i++){
ans = min(ans,dp[row-1][col-1][i]);
}
return ans;
}
};
這題讓我思考了一類題:
1.矩陣
2.每個點具有不同條件下的狀態(k不同)
3.且是由上下左右影響,即每個點變化都會影響周圍點的變化
解決方法:動態規劃+DBS+隊列(剛纔不懂爲啥要用隊列現在想想好像是的!!!,因爲保證每次更新都是最小值,且下次不用更新了!!!)