あなたは遠足に参加するつもりです。格子の高さを表す2次元rows x columns
マップを作成します。グリッドの左上隅から開始し、グリッドの右下隅に移動します(開始番号の添え字に注意してください)。毎回4つの方向のいずれかで上下左右に移動でき、エネルギー消費が最も少ないパスを見つけたいと考えています。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
解释:上图所示路径不需要消耗任何体力。
促す:
rows == heights.length
columns == heights[i].length
1 <= rows, columns <= 100
1 <= heights[i][j] <= 10^6
回答
この質問では、左上隅から右下隅への最短パスが必要です。これは、これら2つのノードを含む最小全域木を見つけると見なすことができます。ここでのパスの長さは、パス全体の隣接するノードの高さの差の最小絶対値を指すことに注意してください。この質問でも、ユニオン検索+クラスカルアルゴリズムを使用して最小全域木を構築できます。
class DSU {
public:
vector<int> parent;
vector<int> rank;
int n;
int count;
DSU(int n) : n(n), count(n) {
parent.resize(n);
rank.resize(n, 1);
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
int find(int i){
if(i != parent[i]){
int temp = find(parent[i]);
parent[i] = temp;
}
return parent[i];
}
bool merge(int i, int j){
int pi = find(i);
int pj = find(j);
if(pi == pj){
return false;
}
else{
if(rank[pi] > rank[pj]){
swap(pi, pj);
}
rank[pj] += rank[pi];
parent[pi] = pj;
count -= 1;
}
return true;
}
bool connected(int i, int j){
return find(i) == find(j);
}
};
class Solution {
public:
static constexpr int directions[4][2] = {
{
1, 0}, {
-1, 0}, {
0, 1}, {
0, -1}};
int minimumEffortPath(vector<vector<int>>& heights) {
if(heights.size() == 0)
return 0;
int m = heights.size();
int n = heights[0].size();
// 构建图
vector<vector<int>> edges;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
int id = i * n + j;
// 从上往下
if(i > 0){
int diff = abs(heights[i][j] - heights[i - 1][j]);
vector<int> temp{
id - n, id, diff};
edges.emplace_back(temp);
}
// 从左往右
if(j > 0){
int diff = abs(heights[i][j] - heights[i][j - 1]);
vector<int> temp{
id - 1, id, diff};
edges.emplace_back(temp);
}
}
}
// Kruskal, 按边长排序
sort(edges.begin(), edges.end(), [](const auto& e1, const auto& e2){
return e1[2] < e2[2];
});
// 并查集构建最小生成树
DSU dsu(m * n);
int result = 0;
for(const auto& e : edges){
int u = e[0];
int v = e[1];
int distance = e[2];
dsu.merge(u, v);
// 说明左上到右下的路径构建完成
if(dsu.connected(0, m * n - 1)){
result = distance;
break;
}
}
return result;
}
};
優先キューを使用して最適化されたDijskraアルゴリズム:
class Solution {
private:
static constexpr int dirs[4][2] = {
{
-1, 0}, {
1, 0}, {
0, -1}, {
0, 1}};
public:
int minimumEffortPath(vector<vector<int>>& heights) {
int m = heights.size();
int n = heights[0].size();
auto tupleCmp = [](const auto& e1, const auto& e2) {
auto&& [x1, y1, d1] = e1;
auto&& [x2, y2, d2] = e2;
return d1 > d2;
};
priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, decltype(tupleCmp)> q(tupleCmp);
q.emplace(0, 0, 0);
vector<int> dist(m * n, INT_MAX);
dist[0] = 0;
vector<int> seen(m * n);
while (!q.empty()) {
auto [x, y, d] = q.top();
q.pop();
int id = x * n + y;
if (seen[id]) {
continue;
}
if (x == m - 1 && y == n - 1) {
break;
}
seen[id] = 1;
for (int i = 0; i < 4; ++i) {
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && max(d, abs(heights[x][y] - heights[nx][ny])) < dist[nx * n + ny]) {
dist[nx * n + ny] = max(d, abs(heights[x][y] - heights[nx][ny]));
q.emplace(nx, ny, dist[nx * n + ny]);
}
}
}
return dist[m * n - 1];
}
};
// 作者:LeetCode-Solution
// 链接:https://leetcode-cn.com/problems/path-with-minimum-effort/solution/zui-xiao-ti-li-xiao-hao-lu-jing-by-leetc-3q2j/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。