p1631.path-with-minimum-effort
https://leetcode-cn.com/problems/path-with-minimum-effort/
keywords: dfs, bfs, 并查集
分析:
首先进行模型抽象,将地图抽象为图论模型。
地图中每个格子抽象为一个节点;每个上下或左右相邻的节点之间对应一条无向边,边权值为两个格子高度差的绝对值。
需要找到一条从左上角到右下角的最短路径,其中路径的长度定义为该路径所有边边权值的最大值。
题解:
方法一:并查集
将所有边按照从小到大顺序排列,依次从中选择一条边,将边的两个节点Union。若加入该边后0号节点和最后一个节点在同一个集合中即存在一条从0号节点到最后一个节点的通路,则该边的权值即为所求。
c++代码:
#include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>
#include <sstream>
using namespace std;
class UFSets{
private:
int maxSize;
int setCount;//连通分量数
vector<int> parent;
public:
UFSets(int size=10):maxSize(size){
parent=vector<int>(size);
for(int i=0;i<size;i++){
parent[i]=-1;
}
setCount=size;
}
~UFSets(){
//delete parent;
}
int& operator[](int i){
return parent[i];
}
int Find(int v){
return parent[v]<0?v:Find(parent[v]);
};//查找v所在并查集的根
bool Union(int v1,int v2){
int r1=Find(v1),r2=Find(v2);
if(r1==r2){//若两个结点在同一个并查集
return false;
}
//不在一个并查集则合并
if(parent[r1]>=parent[r2]){
parent[r2]+=parent[r1];
parent[r1]=r2;
}
else{
parent[r1]+=parent[r2];
parent[r2]=r1;
}
setCount--;
return true;
};//将并查集r1和并查集r2合并
int size(){
return setCount;
}
};
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(j<n-1){
edges.push_back(tuple<int,int,int>(id,id+1,abs(heights[i][j]-heights[i][j+1])));
}
if(i<m-1){
edges.push_back(tuple<int,int,int>(id,id+n,abs(heights[i][j]-heights[i+1][j])));
}
}
}
sort(edges.begin(),edges.end(),[](tuple<int,int,int> e1,tuple<int,int,int> e2){
return get<2>(e1)<get<2>(e2);
});
UFSets g(m*n);
for(auto &e : edges){
g.Union(get<0>(e),get<1>(e));
if(g.Find(0)==g.Find(m*n-1)){
return get<2>(e);
}
}
}
int main(){
vector<vector<int> >heights;
string temp;
while(getline(cin,temp)){
istringstream in(temp);
int t;
vector<int> ts;
while(in>>t){
ts.push_back(t);
}
heights.push_back(ts);
}
cout<<minimumEffortPath(heights);
return 0;
}
方法二:二分查找
找到一个最小x,使得存在一条从0号节点到最后一个节点的通路。
利用二分查找依次选择x;利用dfs或bfs查找通路。
c++代码:(转自leetcode)
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();
int left = 0, right = 999999, ans = 0;
while (left <= right) {
int mid = (left + right) / 2;
queue<pair<int, int>> q;
q.emplace(0, 0);
vector<int> seen(m * n);
seen[0] = 1;
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
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 && !seen[nx * n + ny] && abs(heights[x][y] - heights[nx][ny]) <= mid) {
q.emplace(nx, ny);
seen[nx * n + ny] = 1;
}
}
}
if (seen[m * n - 1]) {
ans = mid;
right = mid - 1;
}
else {
left = mid + 1;
}
}
return ans;
}
};
思考:
做题的时候第一反应是dfs或bfs,想暴力地找到所有路径进行权值比较,从正面突破。。。。
没有想到反向突破,即检查某个x是否符合要求。

浙公网安备 33010602011771号