LeetCode刷题(每日一题) --1631. 最小体力消耗路径(并查集)

题目

你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。

一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。

请你返回从左上角走到右下角的最小 体力消耗值 。

示例 1:
在这里插入图片描述

输入:heights = [[1,2,2],[3,8,2],[5,3,5]]
输出:2
解释:路径 [1,3,5,3,5]
连续格子的差值绝对值最大为 2 。 这条路径比路径 [1,2,2,2,5] 更优,因为另一条路径差值最大值为 3 。

示例 2:

在这里插入图片描述

输入:heights = [[1,2,3],[3,8,4],[5,3,5]]
输出:1
解释:路径 [1,2,3,4,5]
的相邻格子差值绝对值最大为 1 ,比路径 [1,3,5,3,5] 更优。

示例 3:
在这里插入图片描述

输入:heights =
[[1,2,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]]
输出:0
解释:上图所示路径不需要消耗任何体力。

提示:

rows == heights.length columns == heights[i].length 1 <= rows, columns
<= 100 1 <= heights[i][j] <= 106

解答

思路

  • 首先可以将整个放个看成一个带权重的无向图,每个点之间的绝对值差就是图中像相邻点的边的权重
  • 将每个边进行排序后,可得到边的升序序列
  • 将这些边一次从小到大加入并查集中,知道,左上角与右下角连通

代码

// 并查集模板
class UF
{
public:
    vector<int> uf;
    vector<int> rank;
    vector<int> size;
    int count;

    UF(int number):uf(vector<int>(number)),rank(vector<int>(number)),size(number,1),count(number)
    {
        iota(uf.begin(), uf.end(),0);
        iota(rank.begin(),rank.end(),0);
    }

    int Find(int index)
    {
        return index==uf[index]?index:uf[index]=Find(uf[index]);
    }

    bool Union(int index1,int index2)
    {
        int find1 = Find(index1);
        int find2 = Find(index2);
        if (find1!=find2)
        {
            if(rank[find1]<rank[find2]) swap(find1,find2);
            uf[find2] = find1;
            if(rank[find1]==rank[find2]) rank[find1]+=1;
            size[find1]+=size[find2];
            --count;
            return true;
        }
        else return false;
    }

    bool Connect(int index1,int index2)
    {
        return Find(index1)==Find(index2);
    }
};

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;
        });
        UF uf(m*n);
        int ans =0;
        for(const auto [x,y,v]:edges)
        {
            uf.Union(x,y);
            if(uf.Connect(0,m*n-1))
            {
                ans = v;
                break;
            }
        }
        return  ans;
    }
};

要点

  • iota函数:定义在 numeric 头文件中的 iota() 函数模板会用连续的 T 类型值填充序列。前两个参数是定义序列的正向迭代器,第三个参数是初始的 T 值。第三个指定的值会被保存到序列的第一个元素中。保存在第一个元素后的值是通过对前面的值运用自增运算符得到的。当然,这意味着 T 类型必须支持 operator++()。
    简单来说: 就是将第三个参数作为初始值放入第一个参数中,之后通过无限自增1而填充整个容器
  • vector<tuple<int,int,int>> edges的妙用,可以将三个int整型作为一个元组,并且放入vector中的一维
  • edges.emplace_back(id-n,id,abs(heights[i][j]-heights[i-1][j])),tuple容器可以用emplace_back()进行填充。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hanzoe_lwh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值