题目:
You are a hiker preparing for an upcoming hike. You are given heights, a 2D array of size rows x columns, where heights[row][col] represents the height of cell (row, col). You are situated(位于) in the top-left cell, (0, 0), and you hope to travel to the bottom-right cell, (rows-1, columns-1) (i.e., 0-indexed). You can move up, down, left, or right, and you wish to find a route that requires the minimum effort. A route's effort is the maximum absolute difference in heights between two consecutive(连续的) cells of the route.
Return the minimum effort required to travel from the top-left cell to the bottom-right cell.
思路:
这题和泳池水位上升那一题很像,寻找最小高度差可以转化为在可能的差值当中寻找一个最小的可以满足左上角走到右下角的路径。即二分确定一个值,然后利用BFS遍历(路径与路径之间没有公共点)的方法探求是否存在一个成立的路径。解题的过程中发现了之前使用BFS遍历的一个小错误(导致了速度慢几百甚至几千倍)。
另外一种解法是并查集和最小生成树。首先建立一个集合(起始的时候只有左上角的点),然后每次加入一个和这个点相邻的、两点之间距离最短的一个点加入集合,直到右下角的点属于这个集合为止。解题的过程学习了tuple、并查集模板的用法。
题解:
二分+BFS遍历
class Solution {
private:
int row, col;
int answer;
int direct_x[5]={0,0,1,-1};
int direct_y[5]={1,-1,0,0};
public:
int minimumEffortPath(vector<vector<int>>& heights) {
int low=0, high=1000000, diff;
row=heights.size(), col=heights[0].size(), answer=1000001;
while(low<=high){
diff=(low-high)/2+high;
cout<<"difference is: "<<diff<<endl;
if(search(diff, heights)) {
high=diff-1;
answer=min(answer,diff);
}
else low=diff+1;
}
return answer;
}
/* search if there exsist an answer, while max difference is d */
bool search(int d, vector<vector<int>> heights){
vector<vector<int>> been(row,vector<int>(col,0));
pair<int,int> p={0,0}, cur, next;
queue<pair<int,int>> q;
q.push(p);
while(!q.empty()){
cur=q.front(), q.pop();
been[cur.first][cur.second]=true; //???????
//printf("current point is:(%d,%d)\n",cur.first,cur.second);
if(cur.first==row-1 && cur.second==col-1) return true;
for(int i=0;i<5;++i){
next.first=cur.first+direct_x[i];
next.second=cur.second+direct_y[i];
if(next.first<0 || next.first>=row || next.second<0 || next.second>=col) continue;
if(been[next.first][next.second]==true) continue;
if(abs(heights[cur.first][cur.second]-heights[next.first][next.second])>d) continue;
q.push(next);
been[next.first][next.second]=true; // it has to be there! otherwise it would be extrmely slow...
}
}
return false;
}
};
需要注意的是,been数组的更新时间和位置对于代码速度有很大的影响。当been数组在cur 被queue front() 之后才被更新的时候代码效率很低,而在push() 之后更新就会快狠多。以后写BFS遍历的时候也要注意!
并查集:
// 并查集模板
class UnionFind {
public:
vector<int> parent;
vector<int> size;
int n;
// 当前连通分量数目
int setCount;
public:
UnionFind(int _n): n(_n), setCount(_n), parent(_n), size(_n, 1) {
iota(parent.begin(), parent.end(), 0);
}
int findset(int x) {
return parent[x] == x ? x : parent[x] = findset(parent[x]);
}
bool unite(int x, int y) {
x = findset(x);
y = findset(y);
if (x == y) {
return false;
}
if (size[x] < size[y]) {
swap(x, y);
}
parent[y] = x;
size[x] += size[y];
--setCount;
return true;
}
bool connected(int x, int y) {
x = findset(x);
y = findset(y);
return x == y;
}
};
class Solution {
public:
int minimumEffortPath(vector<vector<int>>& heights) {
int m = heights.size();
int n = heights[0].size();
vector<tuple<int, int, int>> edges;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
int id = i * n + j;
if (i > 0) {
edges.emplace_back(id - n, id, abs(heights[i][j] - heights[i - 1][j]));
}
if (j > 0) {
edges.emplace_back(id - 1, id, abs(heights[i][j] - heights[i][j - 1]));
}
}
}
sort(edges.begin(), edges.end(), [](const auto& e1, const auto& e2) {
auto&& [x1, y1, v1] = e1;
auto&& [x2, y2, v2] = e2;
return v1 < v2;
});
UnionFind uf(m * n);
int ans = 0;
for (const auto [x, y, v]: edges) {
uf.unite(x, y);
if (uf.connected(0, m * n - 1)) {
ans = v;
break;
}
}
return ans;
}
};
//转载自LeetCode官方题解