LeetCode1631 - 最小体力消耗 (二分法BFS遍历、并查集)

题目:

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官方题解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值