day60 图论章节刷题Part10(Floyd 算法、A * 算法)

news2024/11/14 4:09:57

Floyd 算法

思路:本题是多源最短路问题,使用Floyd算法求解。Floyd 算法对边的权值正负没有要求,核心思想是动态规划。
我们使用动规五部曲来理解和应用Floyd算法:

1、确定dp数组(dp table)以及下标的含义
我们用 grid数组来存图,那就把dp数组命名为 grid。grid[i][j][k] = m,表示 节点i 到 节点j 以[1…k] 集合为中间节点的最短距离为m。

2、确定递推公式
分两种情况:
(1)节点i 到 节点j 的最短路径经过节点k:grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1]
(2)节点i 到 节点j 的最短路径不经过节点k:grid[i][j][k] = grid[i][j][k - 1]
因为求最短路,取两种情况的最小值: grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1])

3、dp数组初始化
在开始输入边时不经过其他节点,可以把k 赋值为 0,即grid[i][j][0]。同时,本题求的是最小值,grid数组中其他元素数值应该初始为一个最大数。

4、确定遍历顺序
从递推公式:grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1]) 可以看出,我们需要三个for循环,分别遍历i,j 和k。我们把 k =0 的 i 和j 对应的数值都初始化了,再去计算 k = 1 时 i 和 j 对应的数值。这就好比是一个三维坐标,i 和j 是平层,而k 是 垂直向上 的。遍历的顺序是从底向上 一层一层去遍历。所以遍历k 的for循环一定是在最外面。

5、举例推导dp数组

代码如下:

import java.util.*;
public class Main{
    public static void main (String[] args) {
        Scanner scan =new Scanner(System.in);
        int n=scan.nextInt();
        int m=scan.nextInt();
        int[][][] grid=new int[n+1][n+1][n+1];
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++){
                Arrays.fill(grid[i][j],Integer.MAX_VALUE);
            }
        }
        //添加边的权重,初始化
        for(int i=0;i<m;i++){
            int l=scan.nextInt();
            int r=scan.nextInt();
            int val=scan.nextInt();
            grid[l][r][0]=val;
            grid[r][l][0]=val;
        }
        
        for(int k=1;k<=n;k++){
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    if (grid[i][k][k - 1] != Integer.MAX_VALUE && grid[k][j][k - 1] != Integer.MAX_VALUE) {
                        grid[i][j][k] = Math.min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1]);
                    } else {
                        grid[i][j][k] = grid[i][j][k - 1];
                    }
                }
            }
        }
        
        int num=scan.nextInt();
        while(num>0){
            int start=scan.nextInt();
            int end=scan.nextInt();
            num--;
            if(grid[start][end][n]==Integer.MAX_VALUE) System.out.println(-1);
            else System.out.println(grid[start][end][n]);
        }
    }
}

注意:当两个 Integer.MAX_VALUE 值相加时,会导致整数溢出,结果会变成一个非常小的负数,如 -128。

A * 算法(A star算法)

Astar 是一种 广搜或者 dijkstra 的改良版。在搜索最短路的时候, 如果是无权图(边的权值都是1) 那就用广搜,代码简洁,时间效率和 dijkstra 差不多;如果是有权图(边有不同的权值),优先考虑 dijkstra。

Astar 关键在于 启发式函数,也就是影响广搜或者 dijkstra 从容器(队列)里取元素的优先顺序。BFS 是没有目的性的 一圈一圈去搜索, 而 A * 是有方向性的去搜索,关键在于启发式函数。

由于从队列里取出什么元素,接下来就是从哪里开始搜索。所以启发式函数主要影响队列里元素的排序!对队列里节点进行排序,就需要给每一个节点权值,权值F = G(起点达到目前遍历节点的距离) + H(目前遍历的节点到达终点的距离)

本题的图是无权网格状,在计算两点距离通常有如下三种计算方式:

  • 曼哈顿距离,计算方式: d = abs(x1-x2)+abs(y1-y2)
  • 欧氏距离(欧拉距离) ,计算方式:d = sqrt( (x1-x2)^2 + (y1-y2)^2 )
  • 切比雪夫距离,计算方式:d = max(abs(x1 - x2), abs(y1 - y2))

本题,采用欧拉距离才能最大程度体现 点与点之间的距离。使用欧拉距离计算 和 广搜搜出来的最短路的节点数是一样的。

计算出来 F 之后,按照 F 的 大小选择出队列的节点。可以使用 优先级队列,每次出队列就是F最小的节点。

代码如下:

import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {

    // 定义一个存储移动步数的数组
    static int[][] moves = new int[1001][1001];
    // 定义骑士的移动方向
    static int[][] dir = {{-2, -1}, {-2, 1}, {-1, 2}, {1, 2}, {2, 1}, {2, -1}, {1, -2}, {-1, -2}};
    static int b1, b2; // 目标位置

    // 定义骑士的状态
    static class Knight implements Comparable<Knight> {
        int x, y; // 当前坐标
        int g, h, f; // G, H, F 值

        @Override
        public int compareTo(Knight k) { // 重载比较方法,以便优先队列可以排序
            return Integer.compare(this.f, k.f);
        }
    }

    // 估算函数,使用欧几里得距离的平方
    static int Heuristic(Knight k) {
        return (k.x - b1) * (k.x - b1) + (k.y - b2) * (k.y - b2); // 省略开根号以提高精度
    }

    // A* 算法实现
    static void astar(Knight k) {
        PriorityQueue<Knight> que = new PriorityQueue<>(); // 创建优先队列
        que.add(k); // 将起始节点加入队列

        while (!que.isEmpty()) {
            Knight cur = que.poll(); // 取出队列中优先级最高的节点

            // 如果到达目标位置,则结束搜索
            if (cur.x == b1 && cur.y == b2) {
                break;
            }

            // 遍历所有可能的骑士移动
            for (int i = 0; i < 8; i++) {
                Knight next = new Knight(); // 创建下一个节点
                next.x = cur.x + dir[i][0]; // 更新 x 坐标
                next.y = cur.y + dir[i][1]; // 更新 y 坐标

                // 检查下一个位置是否在有效范围内
                if (next.x < 1 || next.x > 1000 || next.y < 1 || next.y > 1000) {
                    continue;
                }

                // 检查该位置是否已经访问过
                if (moves[next.x][next.y] == 0) {
                    moves[next.x][next.y] = moves[cur.x][cur.y] + 1; // 更新步数

                    // 计算 G, H 和 F 值
                    next.g = cur.g + 5; // 统一不开根号以提高精度
                    next.h = Heuristic(next);
                    next.f = next.g + next.h;
                    que.add(next); // 将下一个节点加入队列
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 读取测试用例的数量

        while (n-- > 0) {
            int a1 = scanner.nextInt(); // 起始 x 坐标
            int a2 = scanner.nextInt(); // 起始 y 坐标
            b1 = scanner.nextInt(); // 目标 x 坐标
            b2 = scanner.nextInt(); // 目标 y 坐标

            // 清空步数数组
            for (int i = 0; i < moves.length; i++) {
                for (int j = 0; j < moves[i].length; j++) {
                    moves[i][j] = 0;
                }
            }

            // 初始化起始节点
            Knight start = new Knight();
            start.x = a1;
            start.y = a2;
            start.g = 0;
            start.h = Heuristic(start);
            start.f = start.g + start.h;

            // 执行 A* 算法
            astar(start);

            // 输出到达目标位置的步数
            System.out.println(moves[b1][b2]);
        }
        scanner.close(); // 关闭扫描器
    }
}

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

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

相关文章

Java反序列化之CommonsCollections4、5、7 链的学习

一、前言 前面的文章中&#xff0c;基本把CC链的关键部分学习的差不多了&#xff0c;利用过程也是比较清晰了&#xff0c;接下来把 CommonsCollections 4、5、7 利用链学习下&#xff0c;扩展下思路 二、CommonsCollections4 利用链的学习 运行环境&#xff1a; java 1.8.0_71…

A030-基于Spring boot的公司资产网站设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

qt QVideoWidget详解

1. 概述 QVideoWidget是Qt框架中用于视频播放的控件。它继承自QWidget&#xff0c;并提供了与QMediaPlayer等多媒体播放类集成的功能。QVideoWidget可以嵌入到Qt应用程序的用户界面中&#xff0c;用于显示视频内容。它支持多种视频格式&#xff0c;并提供了基本的视频播放控制…

PG逻辑复制的REPLICA IDENTITY几种设置

前两天同事问了一个PG的错误&#xff0c;创建一张普通表&#xff0c;insert插入正常&#xff0c;但是执行update和delete时&#xff0c;提示这个错误&#xff0c; 代码语言&#xff1a;javascript 复制 SQL 错误 [55000]: ERROR: cannot delete from table "temp_tb&qu…

Flutter 小技巧之 Shader 实现酷炫的粒子动画

在之前的《不一样的思路实现炫酷 3D 翻页折叠动画》我们其实介绍过&#xff1a;如何使用 Shader 去实现一个 3D 的翻页效果&#xff0c;具体就是使用 Flutter 在 3.7 开始提供 Fragment Shader API &#xff0c;因为每个像素都会过 Fragment Shader &#xff0c;所以我们可以通…

<项目代码>YOLOv7 草莓叶片病害识别<目标检测>

YOLOv7是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv7具有更高的…

一文读懂什么是RAG?附MindSpore和MindNLP实现的TinyRAG框架

什么是RAG&#xff1f; 首先我们给出RAG的定义&#xff1a;RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术是一种结合了信息检索&#xff08;Retrieval&#xff09;和生成式模型&#xff08;Generation&#xff09;的人工智能方法。对于用户的Query&#xff…

字节、快手、Vidu“打野”升级,AI视频小步快跑

文&#xff5c;白 鸽 编&#xff5c;王一粟 继9月份版本更新之后&#xff0c;光锥智能从生数科技联合创始人兼CEO唐家渝朋友圈获悉&#xff0c;Vidu大模型将于本周再次进行版本升级&#xff0c;Vidu-1.5版本即将上线。 此版本更新方向仍是重点延伸大模型的泛化能力和主体…

matlab建模入门指导

本文以水池中鸡蛋温度随时间的变化为切入点&#xff0c;对其进行数学建模并进行MATLAB求解&#xff0c;以更为通俗地进行数学建模问题入门指导。 一、问题简述 一个煮熟的鸡蛋有98摄氏度&#xff0c;将它放在18摄氏度的水池中&#xff0c;五分钟后鸡蛋的温度为38摄氏度&#x…

React Query在现代前端开发中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 React Query在现代前端开发中的应用 React Query在现代前端开发中的应用 React Query在现代前端开发中的应用 引言 React Query …

汇总常用的114款AI视频创作工具,堪称运营神器,收藏备用!

随着AI工具的使用起来起广泛&#xff0c;国内各个互联网大厂都开始在圈内出围。过去我们写文案、做视频、拍视频、剪辑视频、画漫画、处理图片等&#xff0c;都需要手工一点一点地精雕细琢。现在通过AI工具&#xff0c;零基础也能做出很多精致的作品。 前面我在上个月的28号分…

在vue中,完成@wangeditor/editor组件的大数据量加载,解决卡顿

背景 简单说一下需求&#xff0c;一个页面中只存在一个Editor组件&#xff0c;但是需要通过选择不同类型展示不同的content的数据&#xff0c;不过直接通过提供的Editor组件加载的时候&#xff0c;在数据量大&#xff08;测试数据226KB&#xff09;的情况下&#xff0c; 切换类…

通义千问API调用测试 (colab-python,vue)

文章目录 代码&#xff08;来自官网&#xff09;colab中用python测试Qwen2.5在官网上查看并确定过期时间这里看到我的免费额度到25年5月在同一个页面&#xff0c;点击API示例 前端调用直接在前端调用的优缺点以vue为例&#xff08;代码是基于官网node.js的代码转换而来&#xf…

使用 Elasticsearch 构建食谱搜索(一)

作者&#xff1a;来自 Elastic Andre Luiz 了解如何使用 Elasticsearch 构建基于语义搜索的食谱搜索。 简介 许多电子商务网站都希望增强其食谱搜索体验。正确使用语义搜索可以让客户根据更自然的查询&#xff08;例如 “something for Valentines Day - 情人节的礼物” 或 “…

微服务各组件整合

nacos 第一步&#xff0c;引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency> 第二步&#xff0c;增加配置 spring:application:name: …

【大数据学习 | HBASE高级】hive操作hbase

一般在查询hbase的数据的时候我们可以直接使用hbase的命令行或者是api进行查询就行了&#xff0c;但是在日常的计算过程中我们一般都不是为了查询&#xff0c;都是在查询的基础上进行二次计算&#xff0c;所以使用hbase的命令是没有办法进行数据计算的&#xff0c;并且对于hbas…

modbus协议 Mthings模拟器使用

进制转换 HEX 16进制 (0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F表示0-15) dec 10进制 n(16进制) -> 10 abcd.efg(n) d*n^0 c*n^1 b*n^2 a*n^3 e*n^-1 f*n^-2 g*n^-3&#xff08;10&#xff09; 10 -> n(16进制) Modbus基础概念 高位为NUM_H&…

列表(list)

一、前言 本次博客主要讲解 list 容器的基本操作、常用接口做一个系统的整理&#xff0c;结合具体案例熟悉自定义内部排序方法的使用。如有任何错误&#xff0c;欢迎在评论区指出&#xff0c;我会积极改正。 二、什么是list list是C的一个序列容器&#xff0c;插入和删除元素…

Sam Altman:年底将有重磅更新,但不是GPT-5!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

zabbix监控端界面时间与服务器时间不对应

1. 修改系统时间 # tzselect Please select a continent, ocean, "coord", or "TZ".1) Africa2) Americas3) Antarctica4) Asia5) Atlantic Ocean6) Australia7) Europe8) Indian Ocean9) Pacific Ocean 10) coord - I want to use geographical coordina…