力扣 1631. 最小体力消耗路径 二分 并查集 dijkstra

54 篇文章 1 订阅
27 篇文章 0 订阅

https://leetcode-cn.com/problems/path-with-minimum-effort/
在这里插入图片描述
思路一:二分,由于格子的取值范围为 [ 1 , 1 0 6 ] [1,10^6] [1,106],所以最终结果的取值范围一定 [ 0 , 1 0 6 ] [0,10^6] [0,106]之内,我们可以二分最终结果的值,然后看在这个限制条件下能否从左上角走到右下角,再依据结果缩小二分范围。

class Solution {
public:

    int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}};

    bool bfs(vector<vector<int>>& heights,int inf)
    {
        using pr=pair<int,int>;
        queue<pr> q;
        int n=heights.size(),m=heights[0].size();
        vector<vector<bool>> vis(n,vector<bool>(m));
        q.push(pr(0,0));
        vis[0][0]=1;
        while(!q.empty())
        {
            pr fon=q.front();
            if(fon.first==n-1&&fon.second==m-1)
                return 1;
            q.pop();
            for(int i=0;i<4;i++)
            {
                int dx=fon.first+dir[i][0];
                int dy=fon.second+dir[i][1];
                if(dx>=0&&dx<n&&dy>=0&&dy<m&&!vis[dx][dy]&&abs(heights[dx][dy]-heights[fon.first][fon.second])<=inf)
                    q.push(pr(dx,dy)),vis[dx][dy]=1;
            }
        }
        return 0;
    }

    int minimumEffortPath(vector<vector<int>>& heights) {
        int l=0,r=1e6,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(bfs(heights,mid))
                r=mid-1;
            else
                l=mid+1;
        }
        return l;
    }
};

思路二:并查集,类似于最小生成树的做法,我们可以假设每个点都和周围相邻点有一条边,权值即为二者的绝对值,然后对边集进行排序,从小到大做并操作,直到左上角和右下角联通。

class Edge
{
public:
    int x,y,v;
    Edge(int x,int y,int v):x(x),y(y),v(v){}
    bool operator <(const Edge &e)const
    {
        return v<e.v;
    }
};

class Solution {
public:

    vector<int> f;

    int father(int x)
    {
        return f[x]==x?x:f[x]=father(f[x]);
    }

    void uni(int x,int y)
    {
        int fx=father(x);
        int fy=father(y);
        if(fx!=fy)
            f[fx]=fy;
    }

    int minimumEffortPath(vector<vector<int>>& heights) {
        vector<Edge> edges;
        int n=heights.size(),m=heights[0].size(),nm=n*m;
        f.resize(nm);
        for(int i=1;i<nm;i++)
            f[i]=i;
        for(int i=0;i<n;i++)
        {
            int idx_base=i*m;
            for(int j=0;j<m;j++)
            {
                if(j!=m-1)
                    edges.push_back(Edge(idx_base+j,idx_base+j+1,abs(heights[i][j]-heights[i][j+1])));
                if(i!=n-1)
                    edges.push_back(Edge(idx_base+j,idx_base+j+m,abs(heights[i][j]-heights[i+1][j])));
            }
        }
        sort(edges.begin(),edges.end());
        for(Edge& e : edges)
        {
            uni(e.x,e.y);
            if(father(0)==father(nm-1))
                return e.v;
        }
        return 0;
    }
};

思路三:类似于 d i j k s t r a dijkstra dijkstra的的思想,以左上角为起点跑最短路算法,只不过对距离的定义发生了变化(传统是累加,这里是取绝对值的最大值)。由于该图中边是隐式定义的,所以其实就是堆+ b f s bfs bfs

class node
{
public:
    int x,y,dis;
    node(){}
    node(int x,int y,int d):x(x),y(y),dis(d){}
    node(const node& a):x(a.x),y(a.y),dis(a.dis){}
    bool operator <(const node& b)const
    {
        return dis>b.dis;
    }
};

class Solution {
public:
    int minimumEffortPath(vector<vector<int>>& heights) {
        const int inf=0x3f3f3f3f;
        int n=heights.size(),m=heights[0].size();
        int dir[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
        vector<vector<bool>> vis(n,vector<bool>(m));
        priority_queue<node> q;
        q.push(node(0,0,0));
        while(!q.empty())
        {
            node fon=q.top();
            q.pop();
            if(fon.x==n-1&&fon.y==m-1)
                return fon.dis;
            else if(vis[fon.x][fon.y])
                continue;
            vis[fon.x][fon.y]=1;
            for(int i=0;i<4;i++)
            {
                int dx=fon.x+dir[i][0];
                int dy=fon.y+dir[i][1];
                if(dx>=0&&dx<n&&dy>=0&&dy<m&&!vis[dx][dy])
                    q.push(node(dx,dy,max(fon.dis,abs(heights[dx][dy]-heights[fon.x][fon.y]))));
            }
        }
        return 0;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值