这道题的前一天的每日一题”1631. 最小体力消耗路径“也是相同的解法,只是在条件上有所不同,甚至这道hard比1631的medium还简单一些!
对题目进行抽象,题目要求的从左上角到右下角其实可以抽象为寻找从左上角顶点到右下角的连通路径!很显然这个连通路径可以有多种,题目需要的是所经过的顶点的最大值最小的那条路径!
所以采用图论的方法,将每个每个坐标方格抽象为图中的顶点,将相邻两点间数值大的那个点的值作为边权值,对所有边依旧边权值从小到大排序。对这些边进行并查集Union连接,直到发现连接完当前边后,左上角顶点的parent和右下角顶点的parent相同(说明连通了),这条边的权值就是答案!
并查集代码
class Solution {
public:
static bool cmp(vector<int>& a, vector<int>& b){
return a[0]<b[0];
}
int Find(vector<int>& parent, int x){
if(x!=parent[x])
parent[x] = Find(parent, parent[x]);
return parent[x];
}
void Union(vector<int>& parent, int x, int y){
int x_root = Find(parent, x);
int y_root = Find(parent, y);
if(x_root!=y_root)
parent[x_root] = y_root;
}
int swimInWater(vector<vector<int>>& grid) {
vector<vector<int>> graph;
int N = grid.size();
// 建图
for(int i=0;i<N;i++){
for(int j=1;j<N;j++){
vector<int> edge(3,0);
edge[1] = grid[i][j-1];
edge[2] = grid[i][j];
// 相邻两点间数值大的那个点的值作为边权值
edge[0] = max(edge[1],edge[2]);
graph.push_back(edge);
}
}
for(int i=0;i<N;i++){
for(int j=1;j<N;j++){
vector<int> edge(3,0);
edge[1] = grid[j-1][i];
edge[2] = grid[j][i];
edge[0] = max(edge[1],edge[2]);
graph.push_back(edge);
}
}
// 对所有边依据边权排序
sort(graph.begin(),graph.end(),cmp);
vector<int> parent(N*N, 0);
for(int i=0;i<parent.size();i++)
parent[i] = i;
for(int i=0;i<graph.size();i++){
Union(parent, graph[i][1], graph[i][2]);
if(Find(parent,grid[0][0]) == Find(parent,grid[N-1][N-1]))
return graph[i][0];
}
return 0;
}
};
其实这题最容易想到的是DFS爆破!但是不剪枝的DFS会超时
DFS爆破代码
class Solution {
public:
int N;
int res = INT_MAX;
int swimInWater(vector<vector<int>>& grid) {
N = grid.size();
vector<vector<int>> visited(N,vector<int>(N,0));
int m = INT_MIN;
DFS(grid,visited,0,0,m);
return res;
}
void DFS(vector<vector<int>>& grid, vector<vector<int>>& visited, int i, int j,int m){
if(i<0||j>=N||i>=N||j<0||visited[i][j] == 1)
return;
visited[i][j] = 1;
m = max(m,grid[i][j]);
if(i == N-1 && j == N-1){
visited[i][j] = 0;
res = min(m,res);
return;
}
DFS(grid,visited,i-1,j,m);
DFS(grid,visited,i+1,j,m);
DFS(grid,visited,i,j-1,m);
DFS(grid,visited,i,j+1,m);
visited[i][j] = 0;
}
};
这里我们采用二分法来剪枝!首先我们知道矩阵中值的范围在0到N×N-1之间,我们需要找的答案在这个范围内!我们可以对这个答案进行二分查找,对于中值mid,判断矩阵中是否可以在所经过所有元素的值都小于mid的情况下,从左上角走到右上角,要是可以说明实际的答案还要小于mid,要是不行,说明实际的答案是大于mid的!然后对范围进行缩小,再循环执行上面的步骤,直到找到答案
DFS+二分法
class Solution {
public:
int N;
int res = INT_MAX;
bool flag = false;
int mid = 0;
int swimInWater(vector<vector<int>>& grid) {
N = grid.size();
vector<vector<int>> visited(N,vector<int>(N,0));
int m = INT_MIN;
int left = 0;
int right = N*N-1;
while(left<right){
mid = (left+right)/2;
DFS(grid,visited,0,0,m);
if(flag == true)
right = mid;
else
left = mid+1;
flag = false;
visited = vector<vector<int>>(N,vector<int>(N,0));
}
return left;
}
void DFS(vector<vector<int>>& grid, vector<vector<int>>& visited, int i, int j,int m){
if(i<0||j>=N||i>=N||j<0||visited[i][j] == 1||grid[i][j]>mid||flag == true)
return;
visited[i][j] = 1;
//下面千万不要加上被注释掉的visited[i][j] = 0!这里是关键!
//为什么不加呢?因为要是加上的话,那后续遍历又会再走一次之前走过的但是没有用的路!
if(i == N-1 && j == N-1){
// visited[i][j] = 0;
flag = true;
return;
}
DFS(grid,visited,i-1,j,m);
DFS(grid,visited,i+1,j,m);
DFS(grid,visited,i,j-1,m);
DFS(grid,visited,i,j+1,m);
// visited[i][j] = 0;
}
};
这篇博客介绍了如何利用图论的并查集方法和二分搜索来解决寻找矩阵中从左上角到右下角最小体力消耗路径的问题。对比了暴力DFS解法和优化后的解决方案,强调了剪枝在提高效率方面的重要性。并提供了详细的代码实现。
9100

被折叠的 条评论
为什么被折叠?



