Day62 图论part11

Floyd 算法精讲

Floyd 算法代码很简单,但真正理解起原理 还是需要花点功夫,大家在看代码的时候,会发现 Floyd 的代码很简单,甚至看一眼就背下来了,但我为了讲清楚原理,本篇还是花了大篇幅来讲解。

代码随想录

方法1:三维dp数组

import java.util.*;

public class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int n = sc.nextInt();
        int m = sc.nextInt();
        
        int[][][] grid = new int[n+1][n+1][n+1];
        //grid[i][j][k] = m 表示节点i 到 j ,以[1...k] 集合为中间节点的最短距离为m
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                Arrays.fill(grid[i][j], Integer.MAX_VALUE);
            } 
            grid[i][i][0] = 0;
        }
        
        for(int i = 0; i < m; i++){
            int u = sc.nextInt();
            int v = sc.nextInt();
            int w = sc.nextInt();
            grid[u][v][0] = w;
            grid[v][u][0] = w;
        }
        
        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][j][k-1], grid[i][k][k-1]+grid[k][j][k-1]);
                    }else{
                        grid[i][j][k] = grid[i][j][k-1];// grid[i][j][k]并不会继承grid[i][j][k-1],而是保留为初始值;
                    }
                }
            }
        }
        
        int q = sc.nextInt();
        for(int i = 0; i < q; i++){
           int start = sc.nextInt();
           int end = sc.nextInt();
           if(grid[start][end][n] == Integer.MAX_VALUE){
              System.out.println(-1);
           }else{
              System.out.println(grid[start][end][n]); 
           }
        }
        
    }
    
}

方法2:二维dp数组

import java.util.*;

public class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int n = sc.nextInt();
        int m = sc.nextInt();
        
        int[][] grid = new int[n+1][n+1];
        //grid[i][j][k] = m 表示节点i 到 j ,以[1...k] 集合为中间节点的最短距离为m
        for(int i = 1; i <= n; i++){
            Arrays.fill(grid[i], 10001);
            grid[i][i] = 0;
        }
        
        for(int i = 0; i < m; i++){
            int u = sc.nextInt();
            int v = sc.nextInt();
            int w = sc.nextInt();
            grid[u][v] = w;
            grid[v][u] = w;
        }
        
        for(int k = 1; k <= n; k++){
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= n; j++){
                    grid[i][j] = Math.min(grid[i][j], grid[i][k]+grid[k][j]);
                }
            }
        }
        
        int q = sc.nextInt();
        for(int i = 0; i < q; i++){
           int start = sc.nextInt();
           int end = sc.nextInt();
           if(grid[start][end] == 10001){
              System.out.println(-1);
           }else{
              System.out.println(grid[start][end]); 
           }
        }
        
    }
    
}

总结

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

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

2.确定递推公式

第一种情况:不经过中间节点K,那么

grid[i][j][k] = grid[i][j][k-1]

第二种情况:经过中间节点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] = Math.min(grid[i][j][k-1], grid[i][k][k-1]+grid[k][j][k-1]);

3.dp数组如何初始化:需要初始化K=0的情况,K=0,就是两个节点直接相连,没有中间节点,所以直接赋值边的权值就可以了(双向或者无向需要两个方向初始化,有向图只要一个方向初始化)。然后其他对角元素应该初始化为0,其他元素初始化为边的权值的最大值(10001或者最大整形都可以,10001更加方便,后续不需要考虑溢出的情况)。

4.确定遍历顺序:

 grid[i][j][k] = Math.min(grid[i][j][k-1], grid[i][k][k-1]+grid[k][j][k-1]);

初始化的时候把 k =0 的 i 和j 对应的数值都初始化了,这样才能去计算 k = 1 的时候 i 和 j 对应的数值。这就好比是一个三维坐标,i 和j 是平层,而k是垂直向上的。遍历的顺序是从底向上 一层一层去遍历。所以遍历k 的for循环一定是在最外面,这样才能一层一层去遍历。k 依赖于 k - 1, i 和j 的到并不依赖与 i - 1 或者 j - 1 。所以一定是把k 的for循环放在最外面,才能用上 初始化和上一轮计算的结果了。i和j的遍历顺序就无所谓了。

5.二维的dp数组,就把k这一维度去掉。每次进入新的k,其实都保留着上一轮k的数值,靠着最外层的for循环,来实现对k的滚动。

6.Floyd 算法的时间复杂度相对较高,Floyd 算法适合多源最短路,即 求多个起点到多个终点的多条最短路径。适合 稠密图且源点较多的情况。时间复杂度: O(n^3);如果 源点少,其实可以 多次dijsktra 求源点到终点。Floyd 算法对边的权值正负没有要求,都可以处理

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

一般 笔试或者 面试的时候,不会考察A*, 都是会结合具体业务场景问 A*算法,例如:地图导航,游戏开发 等等。其实基础版的A* 并不难,所以大家不要畏惧,理解本篇内容,甚至独立写出代码,大家可以做到,加油

A * 算法精讲 (A star算法) | 代码随想录

import java.util.*;

public class Main {
    static int[][] moves = new int[1001][1001]; // 记录每个位置的移动次数
    static int[][] dir = { // 马的8个方向
            {-2, -1}, {-2, 1}, {-1, 2}, {1, 2}, 
            {2, 1}, {2, -1}, {1, -2}, {-1, -2}
    };
    static int b1, b2; // 目标位置的x, y坐标

    static class Knight implements Comparable<Knight> {
        int x, y, g, h, f;

        Knight(int x, int y, int g, int h) {
            this.x = x;
            this.y = y;
            this.g = g; // G = 从起点到该节点的路径消耗
            this.h = h; // H = 从该节点到终点的预估消耗
            this.f = g + h; // F = G + H
        }

        @Override
        public int compareTo(Knight k) {
            return Integer.compare(this.f, k.f); // 按照f值从小到大排序
        }
    }

    // 欧拉距离的启发函数(不开根号以提高精度)
    static int heuristic(Knight k) {
        return (k.x - b1) * (k.x - b1) + (k.y - b2) * (k.y - b2);
    }

    static void astar(Knight start) {
        PriorityQueue<Knight> queue = new PriorityQueue<>();
        queue.add(start);

        while (!queue.isEmpty()) {
            Knight cur = queue.poll(); // 取出f值最小的节点

            // 如果到达目标位置,直接退出
            if (cur.x == b1 && cur.y == b2) {
                break;
            }

            for (int[] d : dir) {
                int nx = cur.x + d[0];
                int ny = cur.y + d[1];

                // 检查边界
                if (nx < 1 || nx > 1000 || ny < 1 || ny > 1000) {
                    continue;
                }

                // 如果这个位置没有访问过
                if (moves[nx][ny] == 0) {
                    moves[nx][ny] = moves[cur.x][cur.y] + 1; // 更新移动次数
                    int g = cur.g + 5; // 马走日消耗固定为5
                    int h = heuristic(new Knight(nx, ny, 0, 0));
                    Knight next = new Knight(nx, ny, g, h);
                    queue.add(next); // 加入优先队列
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 测试案例数量
        while (n-- > 0) {
            int a1 = sc.nextInt(), a2 = sc.nextInt(); // 起点坐标
            b1 = sc.nextInt();
            b2 = sc.nextInt(); // 终点坐标
            for (int[] row : moves) {
                Arrays.fill(row, 0); // 初始化moves数组
            }
            Knight start = new Knight(a1, a2, 0, heuristic(new Knight(a1, a2, 0, 0)));
            astar(start);
            System.out.println(moves[b1][b2]); // 输出结果
        }
        sc.close();
    }
}

PriorityQueue<Knight> queue = new PriorityQueue<>();这个PriorityQueue 自动根据 compareTo 方法维护堆的性质或任何自定义比较器的实现。

 @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age); // 按年龄升序排序
    }

//反向比较
@Override
public int compareTo(Knight k) {
    return Integer.compare(k.f, this.f); // 交换位置,k 在前面
}

1.为什么按照 F 值排序?

  • F = G + H 表示从起点经过当前节点到终点的总代价估计值。
  • 按照 F 值排序能够保证优先探索 当前预估代价最小的路径,从而以最快的速度找到最优解。

示例解释

假设:

  • 当前节点 A 的 G=2, H=5, 所以 F=2+5=7。
  • 另一个节点 B 的 G=4, H=2, 所以 F=4+2=6。

如果只按照 H 值排序,会优先选择 A(H = 5):

  • 但 A 的总代价 F=7,并不是最优路径。

按照 F 值排序,会优先选择 B(F = 6),更接近最终的最优路径。

核心思路就是从队列里面优先弹出F值更小的元素,那么使用优先级队列就可以做到。Java 的优先级队列 (PriorityQueue) 默认是小顶堆。这意味着在队列中,优先级最低的元素(数值最小的元素)会排在队首,即最先被弹出。

2.moves 数组的作用是 记录某个棋盘位置是否已经访问过,以及该位置从起点到当前的 步数

3.Astar 是一种 广搜的改良版。 或者是 dijkstra 的改良版。如果是无权图(边的权值都是1) 那就用广搜。如果是有权图(边有不同的权值),优先考虑 dijkstra。Astar 关键在于 启发式函数, 也就是 影响 广搜或者 dijkstra 从 容器(队列)里取元素的优先顺序。

最短路算法总结篇

最各个最短路算法有个全面的了解

最短路算法总结篇 | 代码随想录

图论总结

图论总结篇 | 代码随想录

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

活动预告 | Microsoft Azure 在线技术公开课:使用 Azure OpenAI 服务构建生成式应用

课程介绍 通过 Microsoft Learn 免费参加 Microsoft Azure 在线技术公开课&#xff0c;掌握创造新机遇所需的技能&#xff0c;加快对 Microsoft Cloud 技术的了解。参加我们举办的“使用 Azure OpenAI 服务构建生成式应用”活动&#xff0c;了解如何使用包括 GPT 在内的强大的…

vue使用el-select下拉框自定义复选框

在 Vue 开发中&#xff0c;高效且美观的组件能极大地提升用户体验和开发效率。在vue中使用elementplus 的 el-select下拉框实现了一个自定义的多选下拉框组件。 一、代码功能概述 这段代码创建了一个可多选的下拉框组件&#xff0c;通过el-select和el-checkbox-group结合的方…

vue3之小白入门篇

感觉vue这种东西的学习门槛不低&#xff0c;如果原来是用mvc方式编程的话。 在开始想以一个较完整的demo系统开发来学习时&#xff0c;却发现登录页面是个问题。 这是因为网上教程很少从一个小白的角度来设计思考一个完整的系统需要哪些知识&#xff0c;面临哪些问题&#xf…

python版本的Selenium的下载及chrome环境搭建和简单使用

针对Python版本的Selenium下载及Chrome环境搭建和使用&#xff0c;以下将详细阐述具体步骤&#xff1a; 一、Python版本的Selenium下载 安装Python环境&#xff1a; 确保系统上已经安装了Python 3.8及以上版本。可以从[Python官方网站]下载并安装最新版本的Python&#xff0c;…

PyCharm安装激活教程(Jetbrains其它软件可参考)

PyCharm安装激活教程 PyCharm安装激活教程1.python基础环境安装配置1.1 下载及安装 2.PyCharm安装及激活教程2.1 学生/教师安装&#xff08;有学信网/edu.cn邮箱&#xff09;及激活2.1.1 安装2.1.2 激活 2.2 非教育用户安装及激活2.2.1 安装2.2.2 激活 参考文献 PyCharm安装激活…

第 29 章 - ES 源码篇 - 网络 IO 模型及其实现概述

前言 本文介绍了 ES 使用的网络模型&#xff0c;并介绍 transport&#xff0c;http 接收、响应请求的代码入口。 网络 IO 模型 Node 在初始化的时候&#xff0c;会创建网络模块。网络模块会加载 Netty4Plugin plugin。 而后由 Netty4Plugin 创建对应的 transports&#xff0…

如何通过 Konga 可视化界面配置 Kong Key-Auth 实现Open API Key 认证

言简意赅的讲解 Konga 可视化配置 Kong Key-Auth解决的痛点 在现代微服务架构中&#xff0c;API 网关常常用于集中管理 API 的认证和授权。Kong 是一款非常流行的 API 网关&#xff0c;它提供了丰富的插件支持&#xff0c;而 Key Authentication&#xff08;Key-Auth&#xff…

C++——deque的了解和使用

目录 引言 标准库中的deque 一、deque的基本概念 二、deque的常用接口 1.deque的迭代器 2.deque的初始化 3.deque的容量操作 3.1 有效长度和容量大小 3.2 有效长度和容量操作 4.deque的访问操作 5.deque的修改操作 三、deque的应用场景 结束语 引言 在C中&#x…

使用R语言绘制交互地图

在现代地理信息系统&#xff08;GIS&#xff09;应用中&#xff0c;交互地图成为了数据展示的重要工具。相比传统的静态地图&#xff0c;交互地图不仅能够更生动地呈现空间数据&#xff0c;还能增强用户的参与感和数据探索性。本文将介绍如何使用R语言绘制交互地图&#xff0c;…

支持向量机入门指南:从原理到实践

目录 1 支持向量机的基本概念 1.2 数学表达 2 间隔与支持向量 2.1 几何间隔 2.2 支持向量的概念 2.3 规范化超平面 2.4 支持向量的深入分析 2.4.1 支持向量的特征 2.4.2 支持向量的作用 2.4.3 支持向量的代数表示 2.5 KKT条件 3 最优化问题 3.1 问题的形成 3.2 规…

【时时三省】(C语言基础)动态内存函数calloc

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 calloc calloc函数也用来动态内存分配 原型如下: void* calloc&#xff08;size&#xff3f;t num, size&#xff3f;t size&#xff09;&#xff1b; 它们两个的区别是 它是需要两个参数…

Flutter中添加全局防护水印的实现

随着版权意识的加强&#xff0c;越来越多的应用开始在应用内部增加各种各样的水印信息&#xff0c;防止核心信息泄露&#xff0c;便于朔源。 效果如下&#xff1a; 在Flutter中增加全局水印的方式&#xff0c;目前有两种实现。 方案一&#xff0c;在native层添加一个遮罩层&a…

uniapp - 小程序实现摄像头拍照 + 水印绘制 + 反转摄像头 + 拍之前显示时间+地点 + 图片上传到阿里云服务器

前言 uniapp&#xff0c;碰到新需求&#xff0c;反转摄像头&#xff0c;需要在打卡的时候对上传图片加上水印&#xff0c;拍照前就显示当前时间日期地点&#xff0c;拍摄后在呈现刚才拍摄的图加上水印&#xff0c;最好还需要将图片上传到阿里云。 声明 水印部分代码是借鉴的…

图像处理-Ch7-小波函数

个人博客&#xff01;无广告观看&#xff0c;因为这节内容太多了&#xff0c;有点放不下&#xff0c;分了三节 文章目录 多分辨率展开(Multi-resolution Expansions)序列展开(Series Expansions)尺度函数(Scaling Function)例&#xff1a;哈尔尺度函数(Haar scaling func)多分…

本地小主机安装HomeAssistant开源智能家居平台打造个人AI管家

文章目录 前言1. 添加镜像源2. 部署HomeAssistant3. HA系统初始化配置4. HA系统添加智能设备4.1 添加已发现的设备4.2 添加HACS插件安装设备 5. 安装cpolar内网穿透5.1 配置HA公网地址 6. 配置固定公网地址 前言 大家好&#xff01;今天我要向大家展示如何将一台迷你的香橙派Z…

Rocky Linux下安装meld

背景介绍&#xff1a; meld是一款Linux系统下的用于 文件夹和文件的比对软件&#xff0c;非常常用&#xff1b; 故障现象&#xff1a; 输入安装命令后&#xff0c;sudo yum install meld&#xff0c;报错。 12-31 22:12:17 ~]$ sudo yum install meld Last metadata expirat…

数据结构与算法之动态规划: LeetCode 337. 打家劫舍 III (Ts版)

打家劫舍 III https://leetcode.cn/problems/house-robber-iii/description/ 描述 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连一番侦察之后&#xff0c;聪明的小…

chatwoot 开源客服系统搭建

1. 准备开源客服系统&#xff08;我是用的Chatwoot &#xff09; 可以选择以下开源客服系统作为基础&#xff1a; Chatwoot: 功能强大&#xff0c;支持多渠道客户对接&#xff0c;&#xff08;支持app&#xff0c;web&#xff09;。Zammad: 现代的开源工单系统。FreeScout: 免…

sentinel-请求限流、线程隔离、本地回调、熔断

请求限流&#xff1a;控制QPS来达到限流的目的 线程隔离&#xff1a;控制线程数量来达到限流的目录 本地回调&#xff1a;当线程被限流、隔离、熔断之后、就不会发起远程调用、而是使用本地已经准备好的回调去提醒用户 服务熔断&#xff1a;熔断也叫断路器&#xff0c;当失败、…

鸿蒙开发-ArkTS中使用Path组件

在ArkTS中使用Path组件&#xff0c;可以按照以下步骤进行&#xff1a; 一、了解Path组件 Path组件用于根据绘制路径生成封闭的自定义形状。该组件从API Version 7开始支持&#xff0c;并随着后续版本的更新可能增加新的功能。Path组件支持多种属性和方法&#xff0c;用于定义…