【代码随想录训练营第42期 Day61打卡 - 图论Part11 - Floyd 算法与A * 算法

news2024/11/13 9:32:39

目录

一、Floyd算法与A * 算法

1、Floyd算法

思想

伪代码

2、 A * 算法

思想

伪代码

二、经典题目

题目一:卡码网 97. 小明逛公园

题目链接

题解:Floyd 算法

题目二:卡码网 127. 骑士的攻击

题目链接

 题解:A * 算法(A-Star)

三、小结


一、Floyd算法与A * 算法

1、Floyd算法

思想

Floyd算法的基本思想是逐步迭代地考虑图中的所有顶点,并更新任意两点之间的最短路径长度。在每一步迭代中,算法检查所有顶点对(i,j),并通过当前考虑的顶点k,看是否能够找到一条从i到j的更短路径。具有能够找到图中任意两点间的最短路径的优势,适合解决多源最短路,即求多个起点到多个终点的多条最短路径

Floyd算法核心思想是动态规划

伪代码
function floydWarshall(weights, V):
    let dist be a V x V array
    for i from 0 to V-1:
        for j from 0 to V-1:
            if i == j:
                dist[i][j] = 0
            else if there is an edge from i to j:
                dist[i][j] = weight of the edge from i to j
            else:
                dist[i][j] = INFINITY

    for k from 0 to V-1:
        for i from 0 to V-1:
            for j from 0 to V-1:
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]

    return dist

2、 A * 算法

入门建议观看视频:【A*寻路算法详解 #A星 #启发式搜索】

思想

A算法(A-Star算法)是一种在图形平面上,有多个节点的路径中,寻找一条从起点到终点的最短路径的算法。它的设计思想主要结合了实际代价和启发式估计,以高效地搜索图形中的最优路径。

核心思想是将启发式搜索和最佳优先搜索结合起来,以寻找从起始点到目标点的最短路径。

伪代码
function A*(start, goal)
    open_list = priority_queue() // 优先队列,按f值排序
    start.g = 0
    start.h = heuristic(start, goal)
    start.f = start.g + start.h
    start.parent = null
    open_list.add(start)

    while not open_list.is_empty()
        current = open_list.pop() // 取出f值最小的节点
        if current == goal
            return reconstruct_path(current)

        closed_list.add(current) // 将当前节点加入封闭列表

        for neighbor in neighbors(current)
            if neighbor in closed_list
                continue
            tentative_g = current.g + distance(current, neighbor)
            if neighbor not in open_list or tentative_g < neighbor.g
                neighbor.parent = current
                neighbor.g = tentative_g
                neighbor.h = heuristic(neighbor, goal)
                neighbor.f = neighbor.g + neighbor.h
                if neighbor not in open_list
                    open_list.add(neighbor)

    return null // 没有找到路径

function reconstruct_path(node)
    path = []
    while node is not null
        path.prepend(node)
        node = node.parent
    return path.reverse() // 反转路径以从起点到终点

function heuristic(node, goal)
    // 返回从node到goal的启发式估计值
end function

function neighbors(node)
    // 返回node的邻居节点列表
end function

function distance(node1, node2)
    // 返回从node1到node2的实际距离
end function

二、经典题目

题目一:卡码网 97. 小明逛公园

题目链接

97. 小明逛公园 (kamacoder.com)

题目描述

小明喜欢去公园散步,公园内布置了许多的景点,相互之间通过小路连接,小明希望在观看景点的同时,能够节省体力,走最短的路径。 

给定一个公园景点图,图中有 N 个景点(编号为 1 到 N),以及 M 条双向道路连接着这些景点。每条道路上行走的距离都是已知的。

小明有 Q 个观景计划,每个计划都有一个起点 start 和一个终点 end,表示他想从景点 start 前往景点 end。由于小明希望节省体力,他想知道每个观景计划中从起点到终点的最短路径长度。 请你帮助小明计算出每个观景计划的最短路径长度。

输入描述

第一行包含两个整数 N, M, 分别表示景点的数量和道路的数量。 

接下来的 M 行,每行包含三个整数 u, v, w,表示景点 u 和景点 v 之间有一条长度为 w 的双向道路。 

接下里的一行包含一个整数 Q,表示观景计划的数量。 

接下来的 Q 行,每行包含两个整数 start, end,表示一个观景计划的起点和终点。

输出描述

对于每个观景计划,输出一行表示从起点到终点的最短路径长度。如果两个景点之间不存在路径,则输出 -1。

输入示例

7 3
2 3 4
3 6 6
4 7 8
2
2 3
3 4

输出示例

4
-1

提示信息

从 2 到 3 的路径长度为 4,3 到 4 之间并没有道路。

1 <= N, M, Q <= 1000.

题解:Floyd 算法

关键在于Floyd算法的实现,即:

使用三重循环来实现Floyd算法:

外层循环变量 k 代表中间节点,内两层循环变量 i 和 j 分别代表起点和终点。

在每一轮循环中,检查通过中间节点 k 从 i 到 j 的路径是否比当前已知的路径更短,如果是,则更新 grid[i][j]。

代码实现:

 for (int k = 1; k <= n; k++) // k代表中间节点
    {
        for (int i = 1; i <= n; i++) // i代表起点
        {
            for (int j = 1; j <= n; j++) // j代表终点
            {
                grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j]); // 更新i到j的最短路径,如果通过k的路径更短,则更新
            }
        }
    }

完整代码如下:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, m, p1, p2, val;
    cin >> n >> m;

    vector<vector<int>> grid(n + 1, vector<int>(n + 1, 10005)); // 创建一个二维向量grid来存储图的邻接矩阵,因为边的最大距离为10**4

    // 读取m条边的信息,并更新邻接矩阵
    for (int i = 0; i < m; i++)
    {
        cin >> p1 >> p2 >> val;
        grid[p1][p2] = val;
        grid[p2][p1] = val; // 注意这里是双向图:需要考虑两个方向
    }
    // 开始 floyd 算法
    for (int k = 1; k <= n; k++) // k代表中间节点
    {
        for (int i = 1; i <= n; i++) // i代表起点
        {
            for (int j = 1; j <= n; j++) // j代表终点
            {
                grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j]); // 更新i到j的最短路径,如果通过k的路径更短,则更新
            }
        }
    }
    // 输出结果
    int Q, start, end;
    cin >> Q;
    while (Q--)
    {
        cin >> start >> end;
        if (grid[start][end] == 10005) // 如果最短路径长度仍然是初始值,表示没有路径
            cout << -1 << endl;
        else
            cout << grid[start][end] << endl;
    }
    return 0;
}

题目二:卡码网 127. 骑士的攻击

题目链接

127. 骑士的攻击 (kamacoder.com)

题目描述

在象棋中,马和象的移动规则分别是“马走日”和“象走田”。现给定骑士的起始坐标和目标坐标,要求根据骑士的移动规则,计算从起点到达目标点所需的最短步数。

棋盘大小 1000 x 1000(棋盘的 x 和 y 坐标均在 [1, 1000] 区间内,包含边界)

输入描述

第一行包含一个整数 n,表示测试用例的数量,1 <= n <= 100。

接下来的 n 行,每行包含四个整数 a1, a2, b1, b2,分别表示骑士的起始位置 (a1, a2) 和目标位置 (b1, b2)。

输出描述

输出共 n 行,每行输出一个整数,表示骑士从起点到目标点的最短路径长度。

输入示例

6
5 2 5 4
1 1 2 2
1 1 8 8
1 1 8 7
2 1 3 3
4 6 4 6

输出示例

2
4
6
5
1
0

提示信息

骑士移动规则如图,红色是起始位置,黄色是骑士可以走的地方。

 题解:A * 算法(A-Star)

关键在于A*算法实现的部分:

在astar函数中,使用优先队列来存储当前的Knight对象;

循环直到优先队列为空,即所有可能的路径都被检查过;

在每次循环中,从优先队列中取出F值最小的Knight对象,并检查是否到达目标位置;

如果未到达目标位置,则遍历8个可能的方向,计算下一个位置的坐标,并检查是否越界或已经被访问过;

如果下一个位置合法且未被访问过,则更新路径消耗,计算F值,并将其加入优先队列;

当到达目标位置时,循环结束。

实现代码:

void astar(const Knight &k)
{
    Knight cur, next;
    que.push(k);
    while (!que.empty())
    {
        cur = que.top();
        que.pop();
        if (cur.x == b1 && cur.y == b2)
            break;
        for (int i = 0; i < 8; i++) // 遍历骑士的8个可能移动方向
        {
            // 得到下一个位置的坐标
            next.x = cur.x + dir[i][0];
            next.y = cur.y + dir[i][1];
            if (next.x < 1 || next.x > 1000 || next.y < 1 || next.y > 1000) // 如果下一个位置越界,则跳过
                continue;
            if (!moves[next.x][next.y]) // 如果下一个位置没有被访问过
            {
                moves[next.x][next.y] = moves[cur.x][cur.y] + 1; // 更新下一个位置的路径消耗
                // 开始计算F
                next.g = cur.g + 5;       // 统一不开根号,这样可以提高精度,马走日,1 * 1 + 2 * 2 = 5
                next.h = Heuristic(next); // 计算当前节点到终点的预估消耗
                next.f = next.g + next.h; // 计算F值
                que.push(next);           // 将下一个位置的Knight对象加入优先队列
            }
        }
    }
}

 完整代码实现:

#include <bits/stdc++.h>
using namespace std;

int moves[1001][1001];                                              // 定义一个二维数组moves,用于存储骑士从起点到当前位置的移动步数
int dir[8][2] = {-2, -1, -2, 1, -1, 2, 1, 2, 2, 1, 2, -1, 1, -2, -1, -2}; // 定义一个方向数组dir,用于存储骑士可能的移动方向
int b1, b2;

struct Knight // 定义一个结构体Knight,用于存储骑士的当前位置和路径消耗等信息
{
    int x, y;
    int g, h, f;
    bool operator<(const Knight &k) const // 重载运算符,用于优先队列的排序
    {
        return k.f < f; // 从小到大排序
    }
};

priority_queue<Knight> que; // 定义一个优先队列que,用于存储Knight对象,并按照f值排序

int Heuristic(const Knight &k) // 欧拉距离
{
    return (k.x - b1) * (k.x - b1) + (k.y - b2) * (k.y - b2); // 统一不开根号,这样可以提高精度
}
// 执行A*算法:参数k是当前的Knight对象
void astar(const Knight &k)
{
    Knight cur, next;
    que.push(k);
    while (!que.empty())
    {
        cur = que.top();
        que.pop();
        if (cur.x == b1 && cur.y == b2)
            break;
        for (int i = 0; i < 8; i++) // 遍历骑士的8个可能移动方向
        {
            // 得到下一个位置的坐标
            next.x = cur.x + dir[i][0];
            next.y = cur.y + dir[i][1];
            if (next.x < 1 || next.x > 1000 || next.y < 1 || next.y > 1000) // 如果下一个位置越界,则跳过
                continue;
            if (!moves[next.x][next.y]) // 如果下一个位置没有被访问过
            {
                moves[next.x][next.y] = moves[cur.x][cur.y] + 1; // 更新下一个位置的路径消耗
                // 开始计算F
                next.g = cur.g + 5;       // 统一不开根号,这样可以提高精度,马走日,1 * 1 + 2 * 2 = 5
                next.h = Heuristic(next); // 计算当前节点到终点的预估消耗
                next.f = next.g + next.h; // 计算F值
                que.push(next);           // 将下一个位置的Knight对象加入优先队列
            }
        }
    }
}

int main()
{
    int n, a1, a2;
    cin >> n;
    while (n--)
    {
        cin >> a1 >> a2 >> b1 >> b2;
        memset(moves, 0, sizeof(moves)); // 每次循环都初始化moves数组为0,确保每个骑士问题都有独立的路径记录
        // 定义起点Knight对象start
        // F = G + H
        // G = 从起点到该节点路径消耗
        // H = 该节点到终点的预估消耗
        Knight start;
        start.x = a1;
        start.y = a2;
        start.g = 0;
        start.h = Heuristic(start);
        start.f = start.g + start.h;
        // 执行A*算法,以起点Knight对象start为起点
        astar(start);
        // 执行A*算法后,清空优先队列que,以便下一次循环使用
        while (!que.empty())
            que.pop();

        cout << moves[b1][b2] << endl; // 输出本次从起点到目标点的路径消耗,即moves[b1][b2]
    }
    return 0;
}

三、小结

这算是图论最后的内容了,难度很大,个人感觉只是理解了大概意思,却难以代码实现,后边二刷的时候会强化理解,继续加油!

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

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

相关文章

基于java的工费医疗报销管理系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

单细胞BisqueRNA和BayesPrism去卷积分析工具简单比较

曾老师发来了一个工具&#xff0c;BisqueRNA&#xff0c;这个工具也可以用于单细胞/bulk数据的反卷积~ 因此本次就对这两个工具简单测评一下 ~ 生信菜鸟团&#xff1a;https://mp.weixin.qq.com/s/3dZQxDdY6M1WwMMcus5Gkg 笔者也曾经写过一个推文简单的介绍过&#xff0c;有…

C++的初阶模板和STL

C的初阶模板和STL 回顾之前的内存管理&#xff0c;我们还要补充一个概念&#xff1a;内存池 也就是定位new会用到的场景&#xff0c;内存池只会去开辟空间。 申请内存也就是去找堆&#xff0c;一个程序中会有很多地方要去找堆&#xff0c;这样子效率会很低下&#xff0c;为了…

必知的PDF转换软件:看2024大学生如何选择

你翻翻你文件的下载记录&#xff0c;是不是PDF文件占了大多数&#xff1f;很多是为了保证页面版式直接收到打印手填再扫描或者直接提交。但是如果能够直接在电脑上编辑之后直接转发或者打印是不是方便了很多&#xff1f;这次我就介绍几款可以进行PDF转换操作的工具&#xff0c;…

高效开发,从暗藏玄机的文件系统开始

4G-Cat.1模组的文件系统关乎数据传输速度、存储效率&#xff0c;以及数据安全性等等诸多因素&#xff0c;在应用开发中极为重要。 本期&#xff0c;我们来学习合宙Air201的实用示例——文件系统的使用 Air201文件系统的使用 合宙Air201资产定位模组——是一个集成超低功耗4G通…

AntFlow系列教程之流程拒绝

这是开源项目AntFlow的一个系统入门使用教程.AntFlow是一款开源免费的企业级低代码工作流引擎.仿照钉钉设计,极大降低流程设计、开发和维护成本。详细介绍请查看历史文章&#xff1a;AntFlow开源仿钉钉低代码工作流平台集成RuoYi版本来啦 流程拒绝和流程同意提交的参数是一样的…

Ubuntu20.04 搜索不到任何蓝牙设备

电脑信息 联想扬天YangTianT4900k 问题描述 打开蓝牙之后&#xff0c;一直转圈&#xff0c;搜索不到任何蓝牙设备 排查 dmesg | grep -i blue 有如下错误&#xff1a; Bluetooth: hci0: RTL: unknown IC info, lmp subver 8852, hci rev 000b, hci ver 000b lsusb 芯片型号如…

MySQL数据库的使用

MySQL数据库的启停 先用管理员身份进入系统终端&#xff0c;然后再在终端中输入命令 启动 net start mysql84&#xff08;你所安装的MySQL版本名称&#xff09; 停止 net stop mysql84 不知道所安装的MySQL是什么版本&#xff1f;&#x1f447; 首先打开cmd命令窗口&…

sqli-labs靶场搭建

下载了一个phpstudy进行搭靶场搭建 然后打开phpstudy安装好php,mysql等环境 正式sqli-labs靶场搭建 第一步&#xff1a;下载源码&#xff1a;https://codeload.github.com/Audi-1/sqli-labs/zip/master 解压后放进网站根目录&#xff0c;进到 sqli-labs的文件夹下&#xff0…

分享6个.NET开源的AI和LLM相关项目框架

前言 现如今AI应用的发展可谓是如火如荼的&#xff0c;它们在各个领域都展现出了巨大的潜力和影响力。今天大姚给大家分享6个.NET开源的AI和LLM相关的项目框架&#xff0c;希望能为大家提供一些参考。如果你有更好的推荐&#xff0c;欢迎RP投稿或文末留言。 https://github.c…

虚拟机之与物理机进行文本的复制粘贴

打开终端&#xff08;快捷键CtrlAltt&#xff09;。&#x1f5a5;️ 安装Open VM Tools&#xff0c;输入以下命令&#xff1a; sudo apt-get install open-vm-tools-desktop -y安装这个工具包可以增强虚拟机的功能&#xff0c;包括支持主机与虚拟机之间的复制粘贴&#xff0c;…

台球瞄准的投掷效应或者耦合效应

https://www.zhihu.com/question/27659022 怪不得理论上算的角度, 实际上打总是便宜, 原来这里面还有两个球之间的摩擦.也就是: 老师&#xff0c;这是您八年前的提问。我个人理解是&#xff1a;目标球会跟着主球往同一个方向走。打个比喻就是“目标球”会坐上“主球”的“火车”…

info 命令:查看命令手册

一、命令简介 在 Linux 系统中&#xff0c;可以使用 man​ 查看普通的帮助手册。还可以使用 info​ 命令阅读 Info 格式的文档。 ​info​ 文档的特点&#xff1a;大量使用超链接&#xff0c;通过方向键将光标移动到链接的文字&#xff0c;按下回车键&#xff0c;就可以切换到…

【齐家网-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

利用教育和参与的力量来推动你的应用程序的成功

在竞争激烈的应用推广领域&#xff0c;脱颖而出需要的不仅仅是华丽的广告和充满活力的视觉效果。真正吸引和留住用户的秘诀在于两个经常被忽视但非常强大的策略&#xff1a;教育和参与。如果做得对&#xff0c;这些元素可以将你的应用程序从单纯的下载转变为用户生活中必备的工…

安泰电压放大器设计方法是什么样的

电压放大器是电子领域中常用的设备&#xff0c;用于将低电压信号放大成高电压信号。电压放大器在信号处理、通信系统、仪器测量、控制系统、医疗设备和研究和实验室等领域都有着广泛的应用。 电压放大器的设计方法主要包括选择合适的放大器拓扑结构、选择适当的放大器参数以及进…

基于 UniApp 平台的学生闲置物品售卖小程序设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

(已解决)vscode如何选择python解释器

文章目录 前言解决方案 前言 有的时候可能有不同版本的编译器&#xff0c;以适用不同年份的项目。所以&#xff0c;怎么在vscode中换python解释器呢&#xff1f; 解决方案 对着要运行的python文件进行右键&#xff0c;比如我是要运行main文件&#xff0c;点击那个命令选项版…

基于区块链的相亲交易系统源码解析

随着区块链技术的成熟与发展&#xff0c;其去中心化、不可篡改的特性逐渐被应用于各行各业。特别是在婚恋市场中&#xff0c;区块链技术的应用为相亲平台带来了新的可能性 。本文将探讨如何利用区块链技术构建一个透明、高效的相亲交易系统&#xff0c;并提供部分源码示例。 区…

OpenCV运动分析和目标跟踪(4)创建汉宁窗函数createHanningWindow()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 此函数计算二维的汉宁窗系数。 createHanningWindow是OpenCV中的一个函数&#xff0c;用于创建汉宁窗&#xff08;Hann window&#xff09;。汉宁…