当前位置: 首页 > news >正文

java-代码随想录第66天|Floyd 算法、A * 算法精讲 (A star算法)

目录

Floyd 算法

97. 小明逛公园

A * 算法精讲 (A star算法)

127. 骑士的攻击

扩展

A * 的缺点


学习链接:A * 算法精讲 (A star算法) | 代码随想录

Floyd 算法

97. 小明逛公园

题目描述

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

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

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

输入描述

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

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

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

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

输出描述

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

本题是多源最短路,即 求多个起点到多个终点的多条最短路径。

Floyd 算法对边的权值正负没有要求,都可以处理

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

        例如我们再求节点1 到 节点9 的最短距离,用二维数组来表示即:grid[1][9],如果最短距离是10 ,那就是 grid[1][9] = 10。

        那 节点1 到 节点9 的最短距离 是不是可以由 节点1 到节点5的最短距离 + 节点5到节点9的最短距离组成呢?即 grid[1][9] = grid[1][5] + grid[5][9]

        节点1 到节点5的最短距离 是不是可以有 节点1 到 节点3的最短距离 + 节点3 到 节点5 的最短距离组成呢?即 grid[1][5] = grid[1][3] + grid[3][5]

以此类推,节点1 到 节点3的最短距离 可以由更小的区间组成。

        那么这样我们是不是就找到了,子问题推导求出整体最优方案的递归关系呢。

        节点1 到 节点9 的最短距离 可以由 节点1 到节点5的最短距离 + 节点5到节点9的最短距离组成, 也可以有 节点1 到节点7的最短距离 + 节点7 到节点9的最短距离的距离组成。

因为是最短路径,所以我们选择一个最小值

1、确定dp数组(dp table)以及下标的含义

grid[i][j][k] = m,表示 节点i 到 节点j 以[1...k] 集合中的一个节点为中间节点的最短距离为m

 这里的k不能单独指某个节点,k 一定要表示一个集合,即[1...k] ,表示节点1 到 节点k 一共k个节点的集合。

2、确定递推公式

我们分两种情况:

        1.节点i 到 节点j 的最短路径经过节点k

        2.节点i 到 节点j 的最短路径不经过节点k

对于第一种情况,grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1]

        节点i 到 节点k 的最短距离 是不经过节点k,中间节点集合为[1...k-1],所以 表示为grid[i][k][k - 1]

        节点k 到 节点j 的最短距离 也是不经过节点k,中间节点集合为[1...k-1],所以表示为 grid[k][j][k - 1]

第二种情况,grid[i][j][k] = grid[i][j][k - 1]

        如果节点i 到 节点j的最短距离 不经过节点k,那么 中间节点集合[1...k-1],表示为 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数组如何初始化

        grid[i][j][k] = m,表示 节点i 到 节点j 以[1...k] 集合为中间节点的最短距离为m。

刚开始初始化k 是不确定的。

        例如题目中只是输入边(节点2 -> 节点6,权值为3),那么grid[2][6][k] = 3,k需要填什么呢?

        把k 填成1,那如何上来就知道 节点2 经过节点1 到达节点6的最短距离是多少 呢。

        所以 只能 把k 赋值为 0,本题 节点0 是无意义的,节点是从1 到 n。

这样我们在下一轮计算的时候,就可以根据 grid[i][j][0] 来计算 grid[i][j][1],此时的 grid[i][j][1] 就是 节点i 经过节点1 到达 节点j 的最小距离了。

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 依赖于 k - 1, i 和j 的到 并不依赖与 i - 1 或者 j - 1 等等。

那么这三个for的嵌套顺序应该是什么样的呢?

        我们来看初始化,我们是把 k =0 的 i 和j 对应的数值都初始化了,这样才能去计算 k = 1 的时候 i 和 j 对应的数值。

        这就好比是一个三维坐标,i 和j 是平层,而k 是 垂直向上 的。遍历的顺序是从底向上 一层一层去遍历。所以遍历k 的for循环一定是在最外面,这样才能一层一层去遍历

public class FloydBase {// public static int MAX_VAL = Integer.MAX_VALUE;// 边的最大距离是10^4(不选用Integer.MAX_VALUE是为了避免相加导致数值溢出)public static int MAX_VAL = 10005; public static void main(String[] args) {// 输入控制Scanner sc = new Scanner(System.in);System.out.println("1.输入N M");int n = sc.nextInt();int m = sc.nextInt();System.out.println("2.输入M条边");// ① dp定义(grid[i][j][k] 节点i到节点j 可能经过节点K(k∈[1,n]))的最短路径int[][][] grid = new int[n + 1][n + 1][n + 1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {for (int k = 0; k <= n; k++) {grid[i][j][k] = grid[j][i][k] = MAX_VAL; // 其余设置为最大值}}}// ② dp 推导:grid[i][j][k] = min{grid[i][k][k-1] + grid[k][j][k-1], grid[i][j][k-1]}while (m-- > 0) {int u = sc.nextInt();int v = sc.nextInt();int weight = sc.nextInt();// 初始化(处理k=0的情况) ③ dp初始化grid[u][v][0] = grid[v][u][0] = weight; }// ④ dp推导:floyd 推导for (int k = 1; k <= n; k++) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {grid[i][j][k] = Math.min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1]);}}}System.out.println("3.输入[起点-终点]计划个数");int x = sc.nextInt();System.out.println("4.输入每个起点src 终点dst");while (x-- > 0) {int src = sc.nextInt();int dst = sc.nextInt();// 根据floyd推导结果输出计划路径的最小距离if (grid[src][dst][n] == MAX_VAL) {System.out.println("-1");} else {System.out.println(grid[src][dst][n]);}}}
}

A * 算法精讲 (A star算法)

127. 骑士的攻击

题目描述

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

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

输入描述

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

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

输出描述

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

        对广搜进行优化,从队列取出的节点尽可能是靠近终点的方向去取,不断地朝着终点地方向取,从队列里面取F的时候进行了排序,取最小的F.

启发式函数:

        G=起点-->当前点

        H=当前点-->终点

        F=G+H

        Astar 关键在于 启发式函数, 也就是 影响 广搜或者 dijkstra 从 容器(队列)里取元素的优先顺序。

注意:BFS 是没有目的性的 一圈一圈去搜索, 而 A * 是有方向性的去搜索

那么 A * 为什么可以有方向性的去搜索,它的如何知道方向呢?

其关键在于 启发式函数

起点达到目前遍历节点的距离 + 目前遍历的节点到达终点的距离 就是起点到达终点的距离。

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

  1. 曼哈顿距离,计算方式: d = abs(x1-x2)+abs(y1-y2)

  2. 欧氏距离(欧拉距离) ,计算方式:d = sqrt( (x1-x2)^2 + (y1-y2)^2 )

  3. 切比雪夫距离,计算方式:d = max(abs(x1 - x2), abs(y1 - y2))

        x1, x2 为起点坐标,y1, y2 为终点坐标 ,abs 为求绝对值,sqrt 为求开根号,

选择哪一种距离计算方式 也会导致 A * 算法的结果不同。

本题,采用欧拉距离才能最大程度体现 点与点之间的距离。

所以 使用欧拉距离计算 和 广搜搜出来的最短路的节点数是一样的。 (路径可能不同,但路径上的节点数是相同的)

计算出来 F 之后,按照 F 的 大小,来选去出队列的节点。

可以使用 优先级队列 帮我们排好序,每次出队列,就是F最小的节点。

扩展

        A * 算法 并不是一个明确的最短路算法,A * 算法搜的路径如何,完全取决于 启发式函数怎么写

        A * 算法并不能保证一定是最短路,因为在设计 启发式函数的时候,要考虑 时间效率与准确度之间的一个权衡。

        虽然本题中,A * 算法得到是最短路,也是因为本题 启发式函数 和 地图结构都是最简单的。

        例如在游戏中,在地图很大、不同路径权值不同、有障碍 且多个游戏单位在地图中寻路的情况,如果要计算准确最短路,耗时很大,会给玩家一种卡顿的感觉。而真实玩家在玩游戏的时候,并不要求一定是最短路,次短路也是可以的 (玩家不一定能感受出来,及时感受出来也不是很在意),只要奔着目标走过去 大体就可以接受。

        所以 在游戏开发设计中,保证运行效率的情况下,A * 算法中的启发式函数 设计往往不是最短路,而是接近最短路的 次短路设计

A * 的缺点

        上述 A * 代码的时候,可以看到 我们想 队列里添加了很多节点,但真正从队列里取出来的 仅仅是 靠启发式函数判断 距离终点最近的节点。

        相对了 普通BFS,A * 算法只从 队列里取出 距离终点最近的节点。

那么问题来了,A * 在一次路径搜索中,大量不需要访问的节点都在队列里,会造成空间的过度消耗。

        IDA * 算法 对这一空间增长问题进行了优化。

另外还有一种场景 是 A * 解决不了的。

        如果题目中,给出 多个可能的目标,然后在这多个目标中 选择最近的目标,这种 A * 就不擅长了, A * 只擅长给出明确的目标 然后找到最短路径。

        如果是多个目标找最近目标(特别是潜在目标数量很多的时候),可以考虑 Dijkstra ,BFS 或者 Floyd。

http://www.dtcms.com/a/469559.html

相关文章:

  • 外贸展示网站多少钱企业画册内容
  • 上门做网站哪里有wordpress调用网页
  • 【部署python网站】宝塔面板 小目标2:实时搜索网上资源文件网站放在服务器上 用AI做一个作品,不断迭代。
  • ubuntu服务器重启,xinference自动加载模型脚本
  • 网站建设服务协议 百度什么网站免费制作
  • 有阿里云的主机了怎么做网站wordpress会务网站模版
  • 深度学习入门(二)——反向传播与向量化推导
  • C++设计模式之行为型模式:状态模式(State)
  • 免费自助建站网站一览网络营销推广方法有哪几种
  • 小伙做网站浦东做网站公司
  • Java对象与字符串相互转化的方式
  • 纪检网站建设计划wordpress 防止被黑
  • MXIC旺宏NOR Flash实现微秒级光形切换
  • 5、docker存储卷
  • docker搭建高性能运营级流媒体服务框架ZLMediaKit——筑梦之路
  • 完美迁移:将 nvm 和 npm 完全安装到 Windows D 盘
  • 从零到一:用 Vue 打造一个零依赖、插件化的 JS 库
  • 创建好git项目仓库后如何将本地项目传上去
  • wordpress图片主题模板下载南山网站优化
  • 做外贸大大小小的网站有哪些新手如何做外贸生意
  • langchain4j+SpringBoot+DashScope(灵积)整合
  • MATLAB实现直流电法和大地电磁法的一维正演计算
  • IBM 开源轻量级多模态文档理解模型 Granite-Docling:258M 参数,精准还原 PDF、截图中的公式、表格与代码
  • Python PDF文档加密与保护:确保你的文件安全
  • 【Conda】Conda虚拟环境配置系统环境变量,Jupter可使用
  • 网站网页和网址的关系湘潭seo
  • 对象集合里的id用逗号拼装几种方式
  • 框架--MybatisPlus
  • Coze源码分析-资源库-编辑数据库-前端源码-核心逻辑与接口
  • TikTok SDE OA 2025 真题解析与秋招趋势