【算法基础:搜索与图论】3.4 求最短路算法(Dijkstrabellman-fordspfaFloyd)

文章目录

  • 求最短路算法总览
  • Dijkstra
    • 朴素 Dijkstra 算法(⭐原理讲解!⭐重要!)(用于稠密图)
      • 例题:849. Dijkstra求最短路 I
        • 代码1——使用邻接表
        • 代码2——使用邻接矩阵
      • 补充:稠密图和稀疏图 & 邻接矩阵和邻接表
    • 堆优化版Dijkstra算法(⭐原理讲解!⭐重要!)用于稀疏图
      • 例题:850. Dijkstra求最短路 II
  • bellman-ford
    • 例题:853. 有边数限制的最短路
      • 为什么需要对 dis 数组进行备份?
  • spfa算法(bellman-ford 算法的优化)
    • 例题:851. spfa求最短路
    • 例题:852. spfa判断负环
  • Floyd(很暴力的三重循环)
    • 例题:854. Floyd求最短路

求最短路算法总览

关于最短路可见:https://oi-wiki.org/graph/shortest-path/
在这里插入图片描述

无向图 是一种 特殊的 有向图。(所以上面的知识地图上没有区分边有向还是无向)

关于存储:稠密图用邻接矩阵,稀疏图用邻接表。
朴素Dijkstra 和 堆优化Dijkstra算法的 选择就在于图 是 稠密的还是稀疏的。

Dijkstra

朴素 Dijkstra 算法(⭐原理讲解!⭐重要!)(用于稠密图)

算法步骤:

有一个集合 s 存储当前已经确定是最短距离的点。

  1. 初始化距离,dis[1] = 0, dis[i] = +∞
  2. for i: 1 ~ n 。 (每次循环确定一个点到起点的最短距离,这样 n 次循环就可以确定 n 个点的最短距离)
    找到不在 s 中的 距离最近的点 t,将其放入 s 中。
    用 t 来更新其它所有点的距离(检查所有从 t 出发可以到达的点 x,是否有 dis[x] > dis[t] + w)

在这里插入图片描述

例题:849. Dijkstra求最短路 I

https://www.acwing.com/activity/content/problem/content/918/
在这里插入图片描述

注意图是有向图,图中可能存在重边和自环,所有边权为正值。
求从 1 号点到 n 号点 的最短距离。

按照朴素 Dijkstra 算法的原理,每次用当前不在 s 中的最短距离节点 t 更新其它所有 t 可以到达的下一个节点,重复这个过程 n 次就可以确定 n 个节点的最短距离。

代码1——使用邻接表

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt();
        // 建图
        List<int[]>[] g = new ArrayList[n + 1];
        Arrays.setAll(g, e -> new ArrayList<>());
        for (int i = 0; i < m; ++i) {
            int x = scanner.nextInt(), y = scanner.nextInt(), z = scanner.nextInt();
            g[x].add(new int[]{y, z});
        }

        // 初始化距离
        int[] dis = new int[n + 1];
        Arrays.fill(dis, Integer.MAX_VALUE);
        dis[1] = 0;

        boolean[] st = new boolean[n + 1];
        for (int i = 1; i < n; ++i) {
            int t = -1;
            // 找到当前不在 s 中的最短距离 t 的位置
            for (int j = 1; j <= n; ++j) {
                if (!st[j] && (t == -1 || dis[j] < dis[t])) t = j;
            }

            if (t == n) break;      // 当前离得最近的就是 n 了,直接返回
            st[t] = true;
            
            // 使用 t 更新所有从 t 出发可以达到的下一个节点
            for (int[] y: g[t]) dis[y[0]] = Math.min(dis[y[0]], dis[t] + y[1]);
        }
        
        if (dis[n] == Integer.MAX_VALUE) System.out.println("-1");
        else System.out.println(dis[n]);
    }
}

代码2——使用邻接矩阵

从题目中可以看出是稠密图,所以使用邻接矩阵效率会更高一些。

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt();
        // 建图  g[i][j]表示从i到j的距离
        int[][] g = new int[n + 1][n + 1];
        for (int[] ints : g) Arrays.fill(ints, 0x3f3f3f3f);
        for (int i = 0; i < m; ++i) {
            int x = scanner.nextInt(), y = scanner.nextInt(), z = scanner.nextInt();
            g[x][y] = Math.min(g[x][y], z);
        }

        // 初始化各个点到起始点的距离
        int[] dis = new int[n + 1];
        Arrays.fill(dis, Integer.MAX_VALUE);
        dis[1] = 0;

        boolean[] st = new boolean[n + 1];
        for (int i = 1; i < n; ++i) {
            int t = -1;
            // 找到当前不在 s 中的最短距离 t 的位置
            for (int j = 1; j <= n; ++j) {
                if (!st[j] && (t == -1 || dis[j] < dis[t])) t = j;
            }

            if (t == n) break;      // 当前离得最近的就是 n 了,直接返回
            st[t] = true;

            // 使用 t 更新所有从 t 出发可以达到的下一个节点
            for (int j = 1; j <= n; ++j) {
                dis[j] = Math.min(dis[j], dis[t] + g[t][j]);
            }
        }

        if (dis[n] == 0x3f3f3f3f) System.out.println("-1");
        else System.out.println(dis[n]);
    }
}

补充:稠密图和稀疏图 & 邻接矩阵和邻接表

在这里插入图片描述
总结一下:

邻接矩阵的空间复杂度为 O ( n 2 ) O(n^2) O(n2),邻接表的空间复杂度为 O ( n + m ) O(n + m) O(n+m),其中 n 是图中节点的数量,m 是边的数量。

Q:如何判断什么时候是稠密的?
A:当 m m m 接近最大可能边数 n ∗ ( n − 1 ) / 2 n * (n - 1)/2 n(n1)/2 时,那么图通常被视为稠密的。

堆优化版Dijkstra算法(⭐原理讲解!⭐重要!)用于稀疏图

如果是一个稀疏图, O ( n 2 ) O(n^2) O(n2) 的朴素 Dijkstra 算法可能会很慢,因此出现了堆优化版本的 Dijkstra 算法。
在这里插入图片描述

用堆来存储所有点到起点的最短距离,就可以减小整个算法的时间复杂度。

用 t 更新其它点的距离,因为有 m 条边,所以这个操作是 m 次,每次的时间复杂度是 logn,因此一共是 m ∗ log ⁡ n m*\log{n} mlogn。 (所以 m 比较小时,即稀疏图,使用堆优化效果更好)

其实就是用堆来优化了每次找当前和起始点最近的点的过程。(朴素的需要枚举 n)

例题:850. Dijkstra求最短路 II

https://www.acwing.com/activity/content/problem/content/919/

在这里插入图片描述

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt();
        // 建图
        List<int[]>[] g = new ArrayList[n + 1];
        Arrays.setAll(g, e -> new ArrayList<int[]>());
        for (int i = 0; i < m; ++i) {
            int x = scanner.nextInt(), y = scanner.nextInt(), z = scanner.nextInt();
            g[x].add(new int[]{y, z});
        }

        //
        int[] dis = new int[n + 1];
        Arrays.fill(dis, 0x3f3f3f3f);
        dis[1] = 0;
        boolean[] st = new boolean[n + 1];

        // 按照各个节点与初始节点之间距离 从小到大 排序
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
        pq.offer(new int[]{1, 0});
        while (!pq.isEmpty()) {
            int[] cur = pq.poll();
            int x = cur[0], d = cur[1];

            if (st[x]) continue;                // 检查这个节点是否已经用来更新过了
            st[x] = true;

            // 只要被当前节点更新了就放入优先队列中
            for (int[] y: g[x]) {               // 这个循环最多被执行 m 次(因为有 m 条边)
                if (dis[y[0]] > d + y[1]) {
                    dis[y[0]] = d + y[1];
                    pq.offer(new int[]{y[0], dis[y[0]]});
                }
            }
        }
        System.out.println(dis[n] == 0x3f3f3f3f? -1: dis[n]);;
    }
}

bellman-ford

枚举 n 次:
	每次 循环所有边 a, b, w
		dis[b] = min(dis[b], dis[a] + w)

循环完之后, 所有节点会满足 dis[b] <= dis[a] + w。
在这里插入图片描述

对于 n 次循环中的第 k 次循环,求出的是 : 从 起点走 不超过 k 条边 的最短距离。
因此 如果第 n 次循环时有更新,说明图中存在负环。

例题:853. 有边数限制的最短路

https://www.acwing.com/problem/content/description/855/
在这里插入图片描述
注意! : 如果有负权回路,那么最短路就一定不存在了!

bellman-ford 算法可以判断出 图中是否存在负权回路。(但是一般使用 spfa 来判断是否有负环)

Q:这道题为什么必须使用 bellman-ford 算法?
A:因为限制了最多经过 k 条边,即存在边数限制。

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt(), k = scanner.nextInt();
        // 存储所有边
        int[][] edges = new int[m][3];
        for (int i = 0; i < m; ++i) {
            edges[i][0] = scanner.nextInt();
            edges[i][1] = scanner.nextInt();
            edges[i][2] = scanner.nextInt();
        }

        int[] dis = new int[n + 1], last;
        Arrays.fill(dis, 0x3f3f3f3f);
        dis[1] = 0;
        // 限制 k 次。  (k 次就表示最多经过 k 条边)
        for (int i = 0; i < k; ++i) {
            last = Arrays.copyOf(dis, n + 1);       // 将dis数组先备份一下
            for (int j = 0; j < m; ++j) {           // 枚举所有边
                dis[edges[j][1]] = Math.min(dis[edges[j][1]], last[edges[j][0]] + edges[j][2]);
            }
        }

        // 因为存在负权边,而本题的数据范围最多减 500 * 10000。所以和 0x3f3f3f3f/2 比较大小
        System.out.println(dis[n] > 0x3f3f3f3f / 2? "impossible": dis[n]);
    }
}

为什么需要对 dis 数组进行备份?

在这里插入图片描述
因为如果不备份的话可能会发生串联,为了避免串联,每次更新时只用上一次的结果。

比如上图,在第一次循环中 2 的 dis 被更新成了 1,如果不使用备份的话,那么 3 的 dis 会被接着更新为 2,但这并不是我们所期望的, 3 的 dis 被更新成 2 应该是在第 2 次循环时才会发生的事情。

spfa算法(bellman-ford 算法的优化)

相当于对 bellman-ford 算法做了一个优化。

bellman-ford 在每次循环中枚举了所有边,但实际上有些边并不会对松弛有作用,所以 spfa 就是从这一点进行了优化。
使用队列宽搜进行优化)。

在这里插入图片描述

从公式 d i s [ b ] = m i n ( d i s [ b ] , d i s [ a ] + w ) dis[b] = min(dis[b], dis[a] + w) dis[b]=min(dis[b],dis[a]+w) 可以看出,只有当 d i s [ a ] dis[a] dis[a] 变小了,这条边才有可能让 d i s [ b ] dis[b] dis[b] 跟着变小。


算法步骤:
在这里插入图片描述
基本思想:只有我变小了,我后面的人才会跟着变小

队列里面存的是待更新的点,就是等着用来更新其它点的点。

例题:851. spfa求最短路

https://www.acwing.com/activity/content/problem/content/920/
在这里插入图片描述

这一题的数据保证了图中不存在负环。


代码中不再是 n 次循环嵌套 m 次循环的 bellman-ford 算法了,
而是一个队列维护可以用来更新其它节点的节点队列,初始时放入起始节点 1,其余时间每次取出队首的节点即可。
取出一个节点后,枚举它影响的所有其它节点即可,如果其它节点被影响了,就表示可以把这个被影响的节点放入队列中,(不过放进队列之前要先判断一下是否已经在队列中了,防止重复更新)。

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt();
        // 使用邻接表存储
        List<int[]>[] g = new ArrayList[n + 1];
        Arrays.setAll(g, e -> new ArrayList<int[]>());
        for (int i = 0; i < m; ++i) {
            g[scanner.nextInt()].add(new int[]{scanner.nextInt(), scanner.nextInt()});
        }

        // 初始化距离、队列、是否在队列里的状态
        int[] dis = new int[n + 1];
        Arrays.fill(dis, 0x3f3f3f3f);
        dis[1] = 0;
        Queue<Integer> q = new LinkedList<Integer>();
        q.offer(1);
        boolean[] st = new boolean[n + 1];
        st[1] = true;

        while (!q.isEmpty()) {
            int t = q.poll();
            st[t] = false;

            for (int[] y: g[t]) {
                int j = y[0], w = y[1];
                if (dis[j] > dis[t] + w) {
                    dis[j] = dis[t] + w;
                    // 由于 j 变小了,所以它可以被更新,可以放入队列中
                    // 但是放进去之前要先判断已经是否已经在队列中了,防止重复放置
                    if (!st[j]) {
                        q.offer(j);
                        st[j] = true;
                    }
                }
            }
        }

        System.out.println(dis[n] == 0x3f3f3f3f? "impossible": dis[n]);
    }
}

例题:852. spfa判断负环

https://www.acwing.com/problem/content/description/854/

在这里插入图片描述

跟 bellman-ford 算法判断负环的思路差不多,在更新 dis 数组的同时,维护一个 cnt 数组,cnt[x] 表示当前这个最短路的经过的边数。

每次更新 dis[x] 的时候,就把 cnt[x] 更新成 cnt[t] + 1。(因为 x 是从节点 t 更新过来的)。

如果在更新的过程中出现了 cnt[x] >= n,就表示至少经过了 n 条边,即至少经过了 n + 1 个点,这肯定是不合理的,说明存在负环。

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt();
        // 使用邻接表存储
        List<int[]>[] g = new ArrayList[n + 1];
        Arrays.setAll(g, e -> new ArrayList<int[]>());
        for (int i = 0; i < m; ++i) {
            g[scanner.nextInt()].add(new int[]{scanner.nextInt(), scanner.nextInt()});
        }

        System.out.println(spfa(g, n)? "Yes": "No");
    }

    static boolean spfa(List<int[]>[] g, int n) {
        // 初始化距离、队列、是否在队列里的状态
        int[] dis = new int[n + 1], cnt = new int[n + 1];
        Arrays.fill(dis, 0x3f3f3f3f);
        dis[1] = 0;
        boolean[] st = new boolean[n + 1];
        Queue<Integer> q = new LinkedList<Integer>();
        // 是判断是否存在负环,而不是只判断从1开始是否存在负环
        for (int i = 1; i <= n; ++i) {
            q.offer(i);
            st[i] = true;
        }

        while (!q.isEmpty()) {
            int t = q.poll();
            st[t] = false;

            for (int[] y: g[t]) {
                int j = y[0], w = y[1];
                if (dis[j] > dis[t] + w) {
                    dis[j] = dis[t] + w;
                    cnt[j] = cnt[t] + 1;
                    if (cnt[j] >= n) return true;       // 表示有负环
                    // 由于 j 变小了,所以它可以被更新,可以放入队列中
                    // 但是放进去之前要先判断已经是否已经在队列中了,防止重复放置
                    if (!st[j]) {
                        q.offer(j);
                        st[j] = true;
                    }
                }
            }
        }
        return false;       // false表示没有负环
    }
}

Floyd(很暴力的三重循环)

https://oi-wiki.org/graph/shortest-path/#floyd-%E7%AE%97%E6%B3%95

用于求多源汇最短路。可以求任意两个结点之间的最短路。

使用邻接矩阵将原图存储下来,三重循环

d[i][j]

for (int k = 1; k <= n; ++k) {
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			// 看看i直接到j更近还是 经过k之后更近
			d[i][j] = min(d[i][j], d[i][k] + d[k][j]);	
		}
	}
}

原理其实是基于:动态规划

例题:854. Floyd求最短路

https://www.acwing.com/problem/content/856/

在这里插入图片描述

题目数据保证了不存在负权回路。

同样要注意最后各个距离要和 INF / 2 比较而不是和 INF 比较,因为图中可能存在负权。

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(), m = scanner.nextInt(), t = scanner.nextInt(), INF = (int)1e9;
        // 建图
        int[][] g = new int[n + 1][n + 1];
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (i == j) g[i][j] = 0;
                else g[i][j] = INF;
            }
        }
        for (int i = 0; i < m; ++i) {
            int x = scanner.nextInt(), y = scanner.nextInt(), z = scanner.nextInt();
            g[x][y] = Math.min(g[x][y], z);
        }

        // 求多源最短路
        for (int k = 1; k <= n; ++k) {
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= n; ++j) {
                    g[i][j] = Math.min(g[i][j], g[i][k] + g[k][j]);
                }
            }
        }

        // 回答询问
        while (t-- != 0) {
            int x = scanner.nextInt(), y = scanner.nextInt();
            System.out.println(g[x][y] > INF / 2? "impossible": g[x][y]);   // 由于有负权,所以和INF/2比较
        }
    }
}

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

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

相关文章

WPF实战项目十(API篇):引入工作单元UnitOfWork

1、通过github地址&#xff1a;https://github.com/arch/UnitOfWork&#xff0c;下载UnitOfWork的代码&#xff0c;将工作单元部分的代码引用到自己的项目&#xff0c;新增UnitOfWork文件夹。 2、在UnitOfWork文件夹下引用UnitOfWork下的IPagedList.cs、PagedList.cs类&#xf…

iOS--编译连接的过程_2

文章目录 iOS编译&#xff08;一&#xff09;编译器前端 编译器后端执行一次XCode build的流程 IPA包的内容二进制文件的内容iOS Link Map File文件说明1. Link Map File 是什么2. Link Map File 有什么用3. 生成 Link Map File查看Link Map File1&#xff09;路径部分计算机系…

Docker 核心概念深度解析:探索容器、镜像和仓库在Docker生态系统中的重要作用和 应用

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

机器学习算法基础-覃秉丰 笔记版

文章目录 笔记回归sklearn-LASSOsklearn-一元线性回归sklearn-多元线性回归sklearn-岭回归sklearn-弹性网ElasticNetsklearn-逻辑回归sklearn-非线性逻辑回归标准方程法-岭回归梯度下降法-一元线性回归梯度下降法-多元线性回归梯度下降法-逻辑回归梯度下降法-非线性逻辑回归线性…

【Java基础教程】(四十三)多线程篇 · 下:深入剖析Java多线程编程:同步、死锁及经典案例——生产者与消费者,探究sleep()与wait()的差异

Java基础教程之多线程 下 &#x1f539;本节学习目标1️⃣ 线程的同步与死锁1.1 同步问题的引出2.2 synchronized 同步操作2.3 死锁 2️⃣ 多线程经典案例——生产者与消费者&#x1f50d;分析sleep()和wait()的区别&#xff1f; &#x1f33e; 总结 &#x1f539;本节学习目标…

ARM练习

通过汇编语言完成LED1-3循环点亮练习 .text .global _start _start: /**********LED1点灯**************/ /*初始化RCC*/ RCC_INIT:LDR R0,0X50000A28LDR R1,[R0]ORR R1,R1,#(0X1<<4)ORR R2,R1,#(0x1<<5)STR R1,[R0]STR R2,[R0]LED1_INIT:设置输出模式LDR R0,0X5…

企业网络安全合规框架体系

云安全联盟大中华区发布报告《企业网络安全合规框架体系》&#xff08;以下简称报告&#xff09;&#xff0c;该报告对典型业务场景给出了参考实例&#xff0c;供广大甲方单位、集成商、咨询机构参考。 近些年&#xff0c;随着国内网络安全领域相关法律、法规、政策文件、标准规…

数据结构--图的遍历 BFS

数据结构–图的遍历 BFS 树的广度优先遍历 从 1 结点进行 b f s bfs bfs的顺序&#xff1a; 【1】 【2】【3】【4】 【4】【6】【7】【8】 图的广度优先遍历 从 2 号点开始 b f s bfs bfs的顺序&#xff1a; 【2】 【1】【6】 【5】【3】【7】 【4】【8】 树 vs 图 不存在“回…

ChatGPT:人工智能语言模型的革命性进步

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

PHP后台登录功能单账号登录限制

PHP后台登录功能单账号登录限制 单账号登陆是什么第一步创建数据表第二步创建登录页面test2.html第三步创建登录提交test2.php第四步访问后台首页第五步演示 单账号登陆是什么 一个用户只能登录一个账号通常被称为单账号登录限制或单用户单账号限制。这意味着每个用户只能使用…

微服务一 实用篇 - 5.分布式搜索引擎(ElasticSearch基础)

《微服务一 实用篇 - 5.分布式搜索引擎&#xff08;ElasticSearch基础&#xff09;》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《微服务一 实用篇 - 5.分布式搜索引擎&#xff08;ElasticSearch基础&#xff09;》 《微服务一 实用篇 - 5.分布式搜索…

vue实现excel数据下载,后端提供的list由前端转excel并下载

前言,因为项目需求需要,我们需要把后端传来的list转成excel模板,并且下载下来) 之前有用的插件,但是会有少0的情况,如下 所以采用另一个项目用过的方法,最终完美实现效果,如下: 1,首先我们来看下后端提供的数据结构 2,具体前端代码如下 封装的组件,需要的同学直接copy就行(这…

ORACLE实时SQL监控视图

引言 实时的SQL监控&#xff08;Real Time SQL Monitoring&#xff09;是Oracle 11g的一个新特性&#xff0c;它是一项强大的工具&#xff0c;用于监视和分析正在执行的SQL语句的性能和执行计划。该功能允许我们实时地跟踪SQL查询的执行过程&#xff0c;以及了解其资源消耗、等…

PHP登陆/php登录--【强撸项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 上效果图phpStudy 设置导数据库 项目目录如图&#xff1a;页面代码后台代码 这么丑的界面能忍&#xff1f;配套资源作业&#xff1a; 本系列校训 用免费公开视频&#xff0…

macOS 源码编译 Percona XtraBackup

percona-xtrabackup-2.4.28.tar.gz安装依赖 ╰─➤ brew install cmake ╰─➤ cmake --version cmake version 3.27.0brew 安装 ╰─➤ brew update╰─➤ brew search xtrabackup > Formulae percona-xtrabackup╰─➤ brew install percona-xtrabackup╰─➤ xtr…

投个 3D 冰壶,上班玩一玩

本篇文章将介绍如何使用物理引擎和图扑 3D 可视化技术来呈现冰壶运动的模拟。 Oimo.js 物理引擎 Oimo.js 是一个轻量级的物理引擎&#xff0c;它使用 JavaScript 语言编写&#xff0c;并且基于 OimoPhysics 引擎进行了改进和优化。Oimo.js 核心库只有 150K &#xff0c;专门用…

基于扩展(EKF)和无迹卡尔曼滤波(UKF)的电力系统动态状态估计

1 主要内容 该程序对应文章《Power System Dynamic State Estimation Using Extended and Unscented Kalman Filters》&#xff0c;电力系统状态的准确估计对于提高电力系统的可靠性、弹性、安全性和稳定性具有重要意义&#xff0c;虽然近年来测量设备和传输技术的发展大大降低…

2816. 判断子序列

题目链接&#xff1a; 自己的做法&#xff1a; #include <bits/stdc.h>using namespace std;const int N 1e5 10; int a[N], b[N]; int main() {int n, m;bool flag true;scanf("%d%d", &n, &m);for (int i 0; i < n; i) scanf("%d"…

【并发专题】阻塞队列BlockingQueue实战及其原理分析

目录 前置知识队列有界队列、无界队列Queue——队列在JAVA里面的接口 阻塞队列介绍BlockingQueue——阻塞队列在JAVA里面的接口阻塞队列的应用场景JUC包下的阻塞队列 课程内容*一、ArrayBlockingQueue基本介绍应用场景使用示例基本原理数据结构核心源码解析双指针与环形数组 *二…

内存的五大分区(自用水文)

1、堆区&#xff08;heap&#xff09;——由程序员分配和释放&#xff0c; 若程序员不释放&#xff0c;程序结束时一般由操作系统回收。注意它与数据结构中的堆是两回事 2、栈区&#xff08;stack&#xff09;——由编译器自动分配释放 &#xff0c;存放函数的参数值&#xff0…