1631. 最小體力消耗路徑
你準備參加一場遠足活動。給你乙個二維rows x columns
的地圖heights
,其中heights[row][col]
表示格仔(row, col)
的高度。一開始你在最左上角的格仔(0, 0)
,且你希望去最右下角的格仔(rows-1, columns-1)
(注意下標從0開始編號)。你每次可以往上,下,左,右四個方向之一移動,你想要找到耗費體力最小的一條路徑。
一條路徑耗費的體力值是路徑上相鄰格仔之間高度差絕對值的最大值決定的。
請你返回從左上角走到右下角的最小體力消耗值。
示例 1:
輸入:heights = [[1,2,2],[3,8,2],[5,3,5]]
輸出:2
解釋:路徑 [1,3,5,3,5] 連續格仔的差值絕對值最大為 2 。
這條路徑比路徑 [1,2,2,2,5] 更優,因為另一條路徑差值最大值為 3 。
示例 2:
輸入:heights = [[1,2,3],[3,8,4],[5,3,5]]
輸出:1
解釋:路徑 [1,2,3,4,5] 的相鄰格仔差值絕對值最大為 1 ,比路徑 [1,3,5,3,5] 更優。
示例 3:
輸入:heights = [[1,2,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]]
輸出:0
解釋:上圖所示路徑不需要消耗任何體力。
思路1:最樸素的做法當然就是遍歷出每一條路徑,找出最終路徑。我們嘗試做乙個剪枝,如果說我們設定乙個閾值上限,當查詢路徑時發現當前邊權值大於上限,就可以直接剪去當前的遍歷;當整個圖中存在一條滿足閾值要求的邊時,就可以認為該閾值可以滿足,我們的任務就變成了找到一條滿足這個閾值的最小值。因為1 <= heights[i][j] <= 10^6,所以題目所求的閾值上限不會超過10^6,這就是直接搜尋答案的演算法。我們可以加乙個二分優化,設閾值a>b,若存在一條路徑,使得路徑上所有的邊的權值都滿足<=a,自然也就可以滿足<=b。路徑的搜尋可以用dfs或bfs,當然bfs的遍歷次數更少,更推薦bfs。不知道會不會有讀者有疑惑,因為bfs是一種層次擴充套件的方式,可能在失敗的路徑上存在我們需要使用的邊,直接在bfs過程中剪枝會不會丟掉可能滿足要求的邊呢,我們看乙個圖便知:
在上圖中,a-b為紅邊,表示超過的閾值不滿足要求,但是從a->b可以走滿足要求的a-c-d-b,也就是說,從b出發到終點(因為能到達b,說明前面的路徑都滿足要求了)的路徑可以被考慮,自然也就不會漏掉任何乙個可能路徑了。
class solution ;
visited[0][0] = true;
int i, j;
que.push(node);
while(!que.empty()));
}if(i + 1 < n && !visited[i + 1][j] && abs(heights[i + 1][j] - heights[i][j]) <= m ));
}if(j > 0 && !visited[i][j - 1] && abs(heights[i][j - 1] - heights[i][j]) <= m ));
}if(j + 1 < m && !visited[i][j + 1] && abs(heights[i][j + 1] - heights[i][j]) <= m ));}}
return false;
}int minimumeffortpath(vector>& heights) else
}return ans;}};
思路2:貪心思路構圖。題意要求路徑上的權值盡可能地小,我們就盡可能地多用小邊;將所有的邊權按照大小排序,並依次加入並查集中,當邊權為value的邊加入並查集後,發現左上角與右下角連通,則value為答案(因為我們加入的邊權是單調非減的)。構建邊集合的時候,我們只需要從左上遍歷到右下,考慮右和下的邊即可,而不需要對每個點都考慮上下左右(防止重複運算)。
class solution
};edge edge[20010];
int locatetoindex(int x,int y,int n,int m)
int find(int x)
void union(int a,int b)
}int minimumeffortpath(vector>& heights)
}for(i = 0; i < n; ++ i);
if(j + 1 < m) edge[cnt ++] = edge;}}
sort(edge, edge + cnt);
s = 0, o = n * m - 1;
for(i = 0; i < cnt; ++ i)
return 0;}};
思路3:其實本題是對最短路的定義進行的更改,最短路的長度定義為路徑上的邊的最大長度。那如果我們使用最短路演算法呢?單源最短路的經典演算法dijkstra,維護乙個從源點s到當前點v的最短路徑長度陣列dist[v],每次基於已有的最短路徑對其他路徑進行更新。dijkstra基於這樣一種想法,每次dist陣列內部最小的值對應的點v(不考慮已經成為過中繼點的點),從s到它的最短距離是確定為dist[v]。i
1234
56dist[i]15
2634
visited[i]
yesno
yesno
yesno
在如上**中,可以確定源點到1、3、5,當前要稱為中繼點的點是5,2、4、6已經分別使用經過1、3考慮,現在需要考慮的是是否能夠通過5使得他們的路徑長度變小。
這一想法的成立基礎是更新邏輯保持單點不減,也就是說,考慮更多的點,答案不會變小。而對於max操作,也是一樣的,考慮更多的點,答案也不會變小,因此dijkstra可以解決這個問題。我們使用帶單調佇列優化的dijkstra:
class solution
};bool visited[110][110]; // 已經確定dist[v]的點
int minimumeffortpath(vector>& heights) );
while(!pri_que.empty()));
}if(i + 1 < n && !visited[i + 1][j]));
}if(j > 0 && !visited[i][j - 1]));
}if(j + 1 < m && !visited[i][j + 1]));}}
return 0;}};
1631 最小體力消耗路徑
你準備參加一場遠足活動。給你乙個二維 rows x columns 的地圖 heights 其中 heights row col 表示格仔 row,col 的高度。一開始你在最左上角的格仔 0,0 且你希望去最右下角的格仔 rows 1,columns 1 注意下標從 0 開始編號 你每次可以往 上...
1631 最小體力消耗路徑
你準備參加一場遠足活動。給你乙個二維 rows x columns 的地圖 heights 其中 heights row col 表示格仔 row,col 的高度。一開始你在最左上角的格仔 0,0 且你希望去最右下角的格仔 rows 1,columns 1 注意下標從 0 開始編號 你每次可以往 上...
1631 最小體力消耗路徑
你準備參加一場遠足活動。給你乙個二維rows x columns的地圖heights,其中heights row col 表示格仔 row,col 的高度。一開始你在最左上角的格仔 0,0 且你希望去最右下角的格仔 rows 1,columns 1 注意下標從 0 開始編號 你每次可以往 上,下,左...