leetcode 1631. 最小体力消耗路径 二分+BFS、并查集、Dijkstra算法

news2024/10/7 8:32:18

最小体力消耗路径

在这里插入图片描述
题目与水位上升的泳池中游泳类似

二分查找+BFS

首先,采用二分查找,确定一个体力值,再从左上角,进行BFS,查看能否到达右下角,如果不行,二分查找就往大的数字进行查找,如果可以,还要继续往小的数字进行查找,比如示例1,数字10肯定可以到达右下角,但不是最小的体力。

class Solution {
public:
    int dx[4] = { 0,0,-1,1 };
    int dy[4] = { -1,1,0,0 };
    int m, n;
    bool bfs(vector<vector<int>>& heights, vector<vector<int>> exist, int sub)//二分+BFS
    {
        exist[0][0] = 1;
        queue<pair<int, int>> q;
        q.emplace(0, 0);

        while (!q.empty())
        {
            auto [i, j] = q.front();
            q.pop();

            for (int k = 0; k < 4; ++k)//上下左右四个方向
            {
                int newi = i + dx[k];
                int newj = j + dy[k];
                if (newi >= 0 && newi < m && newj >= 0 && newj < n && !exist[newi][newj] && sub >= abs(heights[i][j] - heights[newi][newj]))
                {
                    exist[newi][newj] = 1;

                    if (newi == m - 1 && newj == n - 1)//到达右下角
                    {
                        return true;
                    }

                    q.emplace(newi, newj);
                }
            }
        }
        return false;
    }
    int minimumEffortPath(vector<vector<int>>& heights)
    {
        m = heights.size(), n = heights[0].size();
        vector<vector<int>> exist(m, vector<int>(n, 0));

        int begin = 0, end = 999999;//最大值,由题目给的边界值得出
        int result = 0;
        while (begin <= end)
        {
            int mid = (begin + end) >> 1;
            if (bfs(heights, exist, mid))
            {
                result = mid;
                end = mid - 1;
            }
            else
            {
                begin = mid + 1;
            }
        }

        return result;
    }
};

二分+BFS一样适合于水位上升的题目。

二分+并查集
依然采用二分查找,确定一个值,只不过BFS换成了并查集

并查集:开辟一个数组,存储每个结点的父节点,当二分查找的某一个值,大于某两个点的差值,就将将其中一个点作为另外一个点的父节点,最后,如果可以到达右下角,那么,左上角的父节点就是右下角,此时,二分查找的值就可能是体力最小值。

这里用到了二维转一维

//并查集
class DSU
{
public:
    DSU(int n)
        :parent(vector<int>(n, 0))
    {
        for (int i = 0; i < n; ++i)//父节点先初始化为自己
        {
            parent[i] = i;
        }
    }

    int Find(int pos)
    {
        if (parent[pos] != pos)
            parent[pos] = Find(parent[pos]);//赋值为祖宗结点,减少搜索次数
            //return Find(parent[pos])parent[pos]为父节点

        return parent[pos];
    }

    void Union(int i, int j)
    {
        parent[Find(i)] = Find(j);
    }

    bool check(int i, int j)
    {
        return Find(i) == Find(j);
    }
private:
    vector<int> parent;
};

class Solution {
public:
    int minimumEffortPath(vector<vector<int>>& heights)
    {
        int m = heights.size(), n = heights[0].size();
        int result = 0;
        int begin = 0, end = 999999;//这里每次都得重新连接一遍,所以用二分,跟水池上升的游泳的题目相比
        while (begin <= end)
        {
            int mid = (begin + end) >> 1;
            DSU dsu(m * n);//二维转一维
            for (int i = 0; i < m; ++i)
            {
                for (int j = 0; j < n; ++j)
                {
                    if (i + 1 < m && abs(heights[i + 1][j] - heights[i][j]) <= mid)//下标方格可以到达
                    {
                        dsu.Union(i * n + j, (i + 1) * n + j);
                    }

                    if (j + 1 < n && abs(heights[i][j + 1] - heights[i][j]) <= mid)//右边方格可以到达
                    {
                        dsu.Union(i * n + j, i * n + j + 1);
                    }
                }
            }
            if (dsu.check(0, m * n - 1))
            {
                result = mid;
                end = mid - 1;
            }
            else
            {
                begin = mid + 1;
            }
        }
        return result;
    }
};

在这里插入图片描述
相比水位上升的题目的并查集,这里的并查集并没有那么有趣,因为水位上升的题目的是采用从0遍历到最大值而不是二分查找,再对某一个值进行并查集。因为,由于水位上升的题目的数据是不重复的,所以可以采用哈希表记录每个值的位置,从0到最大值,只要在某个值,左上角和左下角已经连通,就是答案。比如示例一,分别使用哈希表的记录每个数字的位置,遍历水位,当水位为0时,没有可以连接的,但是水位为1时,可以连接0-》1,水位为2时,连接0-》2,水位为3时,连接1-》3,2-》3。如果这里采用二分的话,假如结果是10时,全部都被连通了,要往下查找更小的值的话,就要重新开辟parent数组。

如果,在最小体力消耗路径的题目依然采用遍历,而不是二分查找的话,虽然还是一个parent数组,当体力来到2,体力1可以连接的点已经连接好了,但是你还是避免不了两层循环遍历heights,查看哪里还可以连接,而不是像上面题目那样,直接哈希表确认2的位置,进行上下左右判断是否可以连接。

Dijkstra算法

开辟一个数组,记录源顶点(左上角)到达某一个点的最小体力
当来到一个新的顶点,消耗的体力比记载的小,就要存储起来,并且以这一个点为新起点,更新上下左右的体力值

//Dijkstra算法
class Solution {
public:
    int minimumEffortPath(vector<vector<int>>& heights)
    {
        int m = heights.size(), n = heights[0].size();
        int INF = INT_MAX / 2;
        vector<int> dist(m * n, INF);//记录从源顶点,到达某个顶点的最小体力消耗
        dist[0] = 0;//顶点为0

        queue<tuple<int, int, int>> q;
        q.emplace(0, 0, 0);//分别表示最小体力差值,坐标
        while (!q.empty())
        {
            auto [physical, i, j] = q.front();
            q.pop();

            if (dist[i * n + j] < physical)//已经被处理过里,并且可以用更少的体力到达该位置
                continue;

            if (j + 1 < n)
            {
                int nextPhysical = max(physical, abs(heights[i][j] - heights[i][j + 1]));//到达左边的方格需要的体力
                if (nextPhysical < dist[i * n + j + 1])
                {
                    dist[i * n + j + 1] = nextPhysical;
                    q.emplace(nextPhysical, i, j + 1);
                }
            }

            if (i + 1 < m)
            {
                int nextPhysical = max(physical, abs(heights[i][j] - heights[i + 1][j]));//到达下边的方格需要的体力
                if (nextPhysical < dist[(i + 1) * n + j])
                {
                    dist[(i + 1) * n + j] = nextPhysical;
                    q.emplace(nextPhysical, i + 1, j);
                }
            }

            if (i - 1 >= 0)
            {
                int nextPhysical = max(physical, abs(heights[i][j] - heights[i - 1][j]));//到达上边方格需要的体力
                if (nextPhysical < dist[(i - 1) * n + j])
                {
                    dist[(i - 1) * n + j] = nextPhysical;
                    q.emplace(nextPhysical, i - 1, j);
                }
            }

            if (j - 1 >= 0)
            {
                int nextPhysical = max(physical, abs(heights[i][j] - heights[i][j - 1]));//到达左边方格需要的体力
                if (nextPhysical < dist[i * n + j - 1])
                {
                    dist[i * n + j - 1] = nextPhysical;
                    q.emplace(nextPhysical, i, j - 1);
                }
            }
        }

        return dist[m * n - 1];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1697766.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

终端安全管理系统、天锐DLP(数据泄露防护系统)| 数据透明加密保护,防止外泄!

终端作为企业员工日常办公、数据处理和信息交流的关键工具&#xff0c;承载着企业运营的核心信息资产。一旦终端安全受到威胁&#xff0c;企业的敏感数据将面临泄露风险&#xff0c;业务流程可能遭受中断&#xff0c;甚至整个企业的运营稳定性都会受到严重影响。 因此&#xff…

Java——认识Java

一、介绍 1、起源 Java 是由 Sun Microsystems 于 1995 年推出的一种面向对象的编程语言和计算平台。由詹姆斯高斯林&#xff08;James Gosling&#xff0c;后来被称为Java之父&#xff09;和他的同事们共同研发。后来&#xff0c;Sun 公司被 Oracle&#xff08;甲骨文&#…

【list】list库介绍 + 简化模拟实现

本节博客先对list进行用法介绍&#xff0c;再在库的基础上简化其内容和形式&#xff0c;简单进行模拟实现&#xff0c;有需要借鉴即可。 目录 1.list介绍1.1 list概述1.2相关接口的介绍 2.简化模拟实现3.各部分的细节详述3.1结点3.2迭代器细节1&#xff1a;迭代器用原生指针还是…

【动态规划】斐波那契数列模型(C++)

目录 1137.第N个泰波那契数 解法&#xff08;动态规划&#xff09; 算法流程 1. 状态表⽰&#xff1a; 2. 状态转移⽅程&#xff1a; 3. 初始化&#xff1a; 4. 填表顺序&#xff1a; 5. 返回值&#xff1a; C算法代码 优化&#xff1a; 滚动数组 测试&#xff1a; …

电脑提示请重新安装软件MSVCP140.dll的几种解决方法分享

在日常使用电脑的过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是找不到msvcp140.dll文件&#xff0c;导致软件无法正常启动运行。这个问题可能是由于缺少相应的依赖库或者版本不匹配引起的。下面我将介绍5种解决方法&#xff0c;帮助大家解决这个问题。…

0524_网络编程8

思维导图&#xff1a;

Java基础的语法---StringBuilder

StringBuilder 构造方法 StringBuilder()&#xff1a;创建一个空的StringBuilder实例。 StringBuilder(String str)&#xff1a;创建一个StringBuilder实例&#xff0c;并将其初始化为指定的字符串内容。 StringBuilder(int a): 创建一个StringBuilder实例…

数据结构--《二叉树》

二叉树 1、什么是二叉树 二叉树(Binar Tree)是n(n>0)个结点的优先集合&#xff0c;该集合或者为空集(称为空二叉树)&#xff0c;或者由一个根结点和两颗互不相交的、分别称为根结点的左子树和右子树的二叉树构成。 这里给张图&#xff0c;能更直观的感受二叉树&#xff1…

AJAX初级

AJAX的概念&#xff1a; 使用浏览器的 XMLHttpRequest 对象 与服务器通信 浏览器网页中&#xff0c;使用 AJAX技术&#xff08;XHR对象&#xff09;发起获取省份列表数据的请求&#xff0c;服务器代码响应准备好的省份列表数据给前端&#xff0c;前端拿到数据数组以后&#xf…

手把手教学,一站式教你实现服务器(Ubuntu)Anaconda多用户共享

背景&#xff1a;书接上回&#xff0c;一站式安装Ubuntu及配置服务器手把手教学&#xff0c;一站式安装ubuntu及配置服务器-CSDN博客 在安装及配置好服务器后&#xff0c;因为课题组可能涉及多个用户共用一台服务器&#xff0c;为了防止服务器上代码误删和Anaconda环境管理混乱…

js之图表使用

今天为了给大家演示图表的使用,今天展示下切换图形的修改属性快速修改 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><script src"./js/jquery-3.7.1.js"></script><script src…

Android 使用 adb 列出设备上所有危险权限

步骤1&#xff1a;确定 Android SDK 位置 打开 Android Studio 的设置&#xff0c;并来到 Languages & Frameworks › Android SDK 处&#xff1a; 这里可以看到 Android SDK 目录的位置&#xff1a; 例如&#xff1a;/Users/admin/Library/Android/sdk。 复制这个路径&am…

RAG概述(一):RAG架构的演进

目录 概述 RAG核心步骤 Indexing索引 Retrieval检索 Generation生成​​​​​​​ Native RAG Advanced RAG Modular RAG 参考 概述 RAG&#xff1a;Retrieval-Augmented Generation 检索增强生成。 RAG通过结合LLMs的内在知识和外部数据库的非参数化数据&#xff…

区间合并-leetcode合并石头的最低成本-XMUOJ元素共鸣:深层次的唤醒

题目 思路 话不多说&#xff0c;直接上代码 附上INT_MAX和INT_MIN 【C】详解 INT_MAX 和 INT_MIN&#xff08;INT_MAX 和 INT_MIN是什么&#xff1f;它们的用途是什么&#xff1f;如何防止溢出&#xff1f;&#xff09;_c int max-CSDN博客 代码 /* leetcode合并石头的最低…

未授权访问:Hadoop 未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、通过REST API命令执行 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章…

【机器学习数据可视化-07】波士顿房价预测数据分析

波士顿房价预测&#xff1a;基于数据可视化的深入探索 一、引言   在当今社会&#xff0c;房地产市场作为经济的重要支柱之一&#xff0c;其走势与波动直接影响着国家经济的稳定和人民生活的品质。波士顿&#xff0c;这座历史悠久且充满活力的城市&#xff0c;其房地产市场一…

ElasticSearch学习篇12_《检索技术核心20讲》基础篇

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243 课程分为基础篇、进阶篇、系统案例篇 主要记录企业课程学习过程课程大纲关键点&#xff0c;以文档形式记录笔记。 内容 检索技术&#xff1a;它是更底层的通用技术&#xff0c…

如何用bet快速创建文件夹多个同级文件夹,多层子文件夹

第一种用txt编辑&#xff0c;保存格式改为bat 运行即可 md用来创建文件夹 md空格文件夹名字 或者 md空格文件夹名字\子文件夹名字 第一个创建一个文件夹&#xff0c;或者多个同级文件夹用空格隔开或者用,英文逗号隔开 md 00 md 00 md 11 md 22 md 33 或者 md 00 1…

Python 中别再用 ‘+‘ 拼接字符串了!

当我开始学习 Python 时&#xff0c;使用加号来连接字符串非常直观和容易&#xff0c;就像许多其他编程语言&#xff08;比如Java&#xff09;一样。 然而&#xff0c;很快我意识到许多开发者似乎更喜欢使用.join()方法而不是。 在本文中&#xff0c;我将介绍这两种方法之间的…

Charles抓包App_https_夜神模拟器

Openssl安装 下载安装 下载地址&#xff1a; http://slproweb.com/products/Win32OpenSSL.html 我已经下载好了64位的&#xff0c;也放出来&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Nkur475YK48_Ayq_vEm99w?pwdf4d7 提取码&#xff1a;f4d7 --来自百度网…