思路:迪杰斯特拉最短路径应用到该题,初步思想:将每个空格看作每个节点,相邻的节点之间存在边,边的权值为相邻值的高度差,求左上角到右下角的最小体力消耗,即为类似求原点到终点的路径。(会超越时间限制)
官方思路:(迪杰斯特拉:)
利用优先队列存储节点坐标(x,y)和当前时刻原点到该点的最小体力消耗值(d),优先队列以d值降序排序,(priority_queue每次都是从队尾取元素的,所以使用top()返回的是排序后的最后一个元素。),然后用top取出下一个节点,对该节点的所有邻居节点进行更新。
class Solution {
private:
int dirs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
public:
int minimumEffortPath(vector<vector<int>>& heights) {
if (heights.empty())
return -1;
//类迪杰斯特拉算法 更新起点到每个点的最小体力消耗
int rows = heights.size(); //行数
int colums = heights[0].size(); //列数
int numVertexes = rows*colums;
vector<int> MinConsume(numVertexes,INT_MAX);
vector<int> Flag(numVertexes,0);
auto cmp = [](auto &e1, auto &e2) //priority_queue每次都是从队尾取元素的,所以使用top()返回的是排序后的最后一个元素。
{
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(cmp)> q(cmp);
q.emplace(0,0,0);
MinConsume[0] = 0;
while(!q.empty())
{
auto [x,y,d] = q.top();
q.pop();
int id = x*colums+y;
if ( Flag[id] == 1 )
continue;
if ( x == rows-1 && y == colums-1 )
break;
Flag[id] = 1;
for(int i=0;i<4;i++)
{
int xn = x + dirs[i][0];
int yn = y + dirs[i][1];
int idn = xn*colums+yn;
if (xn>=0 && xn<rows && yn>=0 && yn<colums && max(d,abs(heights[x][y]-heights[xn][yn]))<MinConsume[idn] )
{
MinConsume[idn] = max(d,abs(heights[x][y]-heights[xn][yn]));
q.emplace(xn,yn,MinConsume[idn]);
}
}
}
return MinConsume[rows*colums-1];
}
};
并查集方法:
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); //iota函数将parent容器中从头到尾进行赋值,第一个元素为0,随后自增
}
int findset(int x){
return x == parent[x] ? x : parent[x] = findset(parent[x]);
}
bool unite( int x, int y ){
int fx = findset( x );
int fy = findset( y );
if ( fx == fy )
return false;
if ( size[fx] > size[fy] ){
int temp = fx;
fx = fy;
fy = temp;
}
parent[fx] = fy;
size[fy] += size[fx];
setCount--;
return true;
}
bool isconnect( int x, int y ){
int fx = findset( x );
int fy = findset( y );
return fx == fy;
}
};
class Solution {
private:
int dirs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
public:
int minimumEffortPath(vector<vector<int>>& heights) {
if (heights.empty())
return -1;
int rows = heights.size(); //行数
int colums = heights[0].size(); //列数
int numVertexes = rows*colums;
//并查集方法
vector<tuple<int,int,int>> edges;
//初始化所有的边
for(int i=0;i<rows;i++){
for(int j=0;j<colums;j++){
int id = i*colums+j;
if ( i > 0){
edges.emplace_back( id-colums, id, abs(heights[i-1][j]-heights[i][j]));
}
if ( j > 0){
edges.emplace_back( id-1, id, abs(heights[i][j-1]-heights[i][j]));
}
}
}
//对所有的边进行排序
sort( edges.begin(), edges.end(), [](const auto &e1,const auto &e2){
auto && [x1,y1,d1] = e1;
auto && [x2,y2,d2] = e2;
return d1 < d2;
} );
UnionFind uf(numVertexes);
int result = 0;
//并查集主体函数
for( const auto [x,y,d]:edges ){
uf.unite( x, y );
if ( uf.isconnect( 0, numVertexes-1 ) ){
result = d;
break;
}
}
return result;
}
并查集通用类:UnionFind