一,BFS层次最短路

/*题目描述
题目描述
给出一个 N 个顶点 M 条边的无向无权图,顶点编号为 1∼N。
问从顶点 1 开始,到其他每个点的最短路有几条。
输入格式
第一行包含 2 个正整数 N,M,为图的顶点数与边数。
接下来 M 行,每行 2 个正整数 x,y,表示有一条连接顶点 x 和顶点 y 的边,请注意可能有自环与重边。
输出格式
共 N 行,每行一个非负整数,第 i 行输出从顶点 1 到顶点 i 有多少条不同的最短路,由于答案有可能会很大,
你只需要输出 ans mod 100003 后的结果即可。如果无法到达顶点 i 则输出 0。
https://www.luogu.com.cn/problem/P1144
https://vjudge.net/contest/737432#problem/D
*/#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
const long long mod = 1e5 + 3;int n, m, s, dis[maxn];
long long cnt[maxn];
vector<int> g[maxn];void bfs() {queue<int> que;fill(dis+1, dis+n+1, -1);dis[1] = 0;cnt[1] = 1;que.push(1);while (!que.empty()) {int u = que.front();que.pop();for (auto v : g[u]) {if (dis[v] == -1) {dis[v] = dis[u] + 1;cnt[v] = cnt[u];    // u = 3que.push(v);}else if (dis[v] == dis[u] + 1)  // u == 7cnt[v] = (cnt[v] + cnt[u]) % mod;}}
}int main() {scanf("%d%d", &n, &m);for (int i = 0, u, v; i < m; i++) {scanf("%d%d", &u, &v);g[u].push_back(v);g[v].push_back(u);}bfs();for (int i = 1; i <= n; i++)printf("%lld\n", cnt[i]);return 0;
}

我认为BFS层次最短路最关键的特征有两个,首先是层次(通过队列先入先出的特征来实现),其次是边权为1,如果边权不是1的话那么同样走一步就会有的步伐大有的步伐小,根本无法判断那条路最短。

二,dijkstra单源最短路

/*题目描述
题目描述
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。
输入格式
第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 m 行每行包含三个整数 u,v,w,表示一条 u→v 的,长度为 w 的边。
输出格式
输出一行 n 个整数,第 i 个表示 s 到第 i 个点的最短路径,若不能到达则输出 2^31?1。
https://www.luogu.com.cn/problem/P4779
https://vjudge.net/contest/737432#problem/B
*/#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5, inf = INT_MAX;
int n, m, s, dis[maxn];
bool vis[maxn];
struct Edge {int v, w;
};
vector<Edge> edge[maxn];struct Node {int u, dist;bool operator < (const Node& b) const {return dist > b.dist;  // 小根堆(优先队列默认是大根堆,这里取反)}
};
priority_queue<Node> quenode;void dijkstra(int s) {fill(dis + 1, dis + n + 1, inf);dis[s] = 0;quenode.push({ s, 0 });while (!quenode.empty()) {int u = quenode.top().u;quenode.pop();if (!vis[u]) {  // 已经处理过的节点直接跳过vis[u] = true;for (auto& e : edge[u]) {int v = e.v;int w = e.w;// 松弛操作:如果通过u到v的路径更短,则更新if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;quenode.push({ v, dis[v] });}}}}
}int main() {scanf("%d%d%d", &n, &m, &s);for (int i = 0, u, v, w; i < m; i++) {scanf("%d%d%d", &u, &v, &w);edge[u].push_back({ v, w });}dijkstra(s);for (int i = 1; i <= n; i++)printf("%d ", dis[i]);return 0;
}

反观dijstra在这方面就有优异的表现,dijstra算法通过根据边权进行提前排序,以及优先队列存储被更新过的新路径进行高效且简单地求出单源最短路,但可以说正是因为dijstra的高效,它无法处理负权边的问题,这是因为dijstra高效在 vis 数组会把一些节点视作“永久节点”,即不会出现更短的路径通向该节点因此“永久节点”不会再被更新,但是负权边的存在使得“永久节点”不再永久,有可能会出现比通向该节点的“最短边”更短的边,另外,如果一个环的权值之和是负的那么通过这个环的“代价”将变成“动力”,因此会出现死循环。

三,Floyd算法求多源最短路

/*题目描述
给出一张由 n 个点 m 条边组成的无向图。
求出所有点对 (i,j) 之间的最短路径。
输入格式
第一行为两个整数 n,m,分别代表点的个数和边的条数。
接下来 m 行,每行三个整数 u,v,w,代表 u,v 之间存在一条边权为 w 的边。
输出格式
输出 n 行每行 n 个整数。
第 i 行的第 j 个整数代表从 i 到 j 的最短路径。
https://www.luogu.com.cn/problem/B3647
*/#include <bits/stdc++.h>
using namespace std;
int n, m, dis[105][105];
int main() {cin >> n >> m;for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)dis[i][j] = (i == j) ? 0 : 1e9;while (m--) {int u, v, w;cin >> u >> v >> w;dis[u][v] = dis[v][u] = min(dis[u][v], w);}for (int k = 1; k <= n; k++)for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++)cout << dis[i][j] << " ";cout << endl;}return 0;
}

其实 Floyd 算法更像是动态规划,逻辑上很好理解,dis[i][j] 代表了从 i 到 j 的最短路,然后分类讨论途径第 k 条路径的最短路,但是我更想介绍的是Floyd算法的一类衍生问题,传递闭包。

附:传递闭包

/*题目描述
https://www.luogu.com.cn/problem/B3611
*/
#include <bits/stdc++.h>
using namespace std;
int n, a;
bitset<100> f[100];
// f[i][j] = 1/0 目前能不能从i到j
int main() {scanf("%d", &n);for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {scanf("%d", &a);if (a)f[i][j] = 1;}}for (int i = 0; i < n; i++) // 经过编号 [0,i] 的点for (int j = 0; j < n; j++) // 对于顶点j来说if (f[j][i])f[j] |= f[i];for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++)printf("%d ", (int)f[i][j]);putchar('\n');}return 0;
}

简单来说传递闭包问题就是判断任意两个节点之间是否可以到达,与Floyd相同的地方在于都是多源的问题,不同点在于数组只会存储 0 或 1 ,所以我们可以用bitset优化一下,大约省了几十倍的空间,最核心的段落就是if (f[j][i]) f[j] |= f[i]; 这一判断,当发现 i 与 j 可到达时,i 所能到达的节点 j 也能到达,反之亦然。

四,bellman-ford算法

/*题目描述
https://www.luogu.com.cn/problem/P3385
*/
#include <bits/stdc++.h>
using namespace std;
// bellman-ford判负环
const int maxn = 2e3 + 5, maxm = 3e3 + 5, inf = (1 << 29);int T, n, m, dis[maxn];
struct Edge {int u, v, w;
} e[maxm];int main() {scanf("%d", &T);while (T--) {scanf("%d%d", &n, &m);dis[1] = 0;fill(dis + 2, dis + n + 1, inf);for (int i = 0; i < m; i++)scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);// bellman-fordint c = 0;for (int i = 1; i <= 2 * n; i++) {for (int j = 0; j < m; j++) {int u = e[j].u;int v = e[j].v;int w = e[j].w;if (dis[u] < inf && dis[v] > dis[u] + w)c = i, dis[v] = dis[u] + w;if (w >= 0 && dis[v] < inf && dis[u] > dis[v] + w)c = i, dis[u] = dis[v] + w;}if (c != i)break;}puts(c == 2 * n ? "YES" : "NO");}return 0;
}

理论上来说,同为单源最短路算法,所有dijkstra算法能用的题目bellman-ford也能用,可以理解为一个更容易实现,一个更全面,以上是一个一般的bellman-ford算法,我一般不用,以下是一个队列优化后的bellman-ford算法,它还有一个响当当的名字(SPFA)。

附:SPFA

#include <bits/stdc++.h>
using namespace std;
// SPFA判负环
const int maxn = 2e3 + 5, maxm = 3e3 + 5, inf = (1<<29);int T, n, m, dis[maxn], cnt[maxn];
bool inq[maxn];
struct Edge {int v, w;
};
vector<Edge> g[maxn];void init() {for (int i = 1; i <= n; i++)g[i].clear();
}bool spfa() {queue<int> que;dis[1] = cnt[1] = 0;fill(dis + 2, dis + n + 1, inf);que.push(1);fill(inq + 1, inq + n + 1, false);inq[1] = true;while (!que.empty()) {int u = que.front();que.pop();inq[u] = false;for (auto &g : g[u]) {if (dis[g.v] > dis[u] + g.w) {dis[g.v] = dis[u] + g.w;cnt[g.v] = cnt[u] + 1;if (cnt[g.v] >= n)return true;if (!inq[g.v])inq[g.v] = true, que.push(g.v);}}}return false;
}int main() {scanf("%d", &T);while (T--) {scanf("%d%d", &n, &m);init();for (int i = 0, u, v, w; i < m; i++) {scanf("%d%d%d", &u, &v, &w);g[u].push_back({v, w});if (w >= 0)g[v].push_back({u, w});}puts(spfa() ? "YES" : "NO");}return 0;
}

 bellman-ford 算法和SPFA算法的核心都是“松弛”(意思就是变短),简单点来说就是,只有一个终端节点(队头节点)的最短距离被更新(松弛)过之后,它将作为开始节点入队。因为在一个有 n 个顶点的图中,最短路径最多包含 n-1 条边(否则必然存在环),所以我们只需要开一个数组 cnt 记录每条路径经过几个节点,当 cnt[i] 大于等于 n 时说明存在环,而又由于我们算的是最短路径,正常情况下来讲算最短路是不可能算出环的,如果出现环那就一定是负环,详见代码。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI智能体小白入门指南

AI智能体小白入门指南 ——什么是AI智能体&#xff1f;它们如何工作&#xff1f; 一、AI智能体是什么&#xff1f; AI智能体&#xff08;AI Agent&#xff09;是能感知环境、自主决策并执行动作的人工智能系统。 类比理解&#xff1a;像一个“虚拟机器人”或“数字助手”&#…

《设计模式》策略模式

1.策略模式定义 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一组算法&#xff0c;将每个算法封装起来&#xff0c;并使它们可以相互替换&#xff0c;从而让算法的变化独立于使用它的客户&#xff08;Client&#xff09;。 换…

AWS DMS 深度解析:从迁移任务到复制任务 - 全流程指南与最佳实践

AWS Database Migration Service (DMS) 是一项强大的云服务,用于在源数据库和目标数据库之间安全地迁移数据。其核心优势在于支持几乎零停机时间的迁移,这主要归功于其“变更数据捕获 (CDC)”功能。理解迁移任务 (Migration Task) 和复制任务 (Replication Task) 的关系与操作…

国企社招 | 中国邮政2025年社会招聘开启

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 原文链接&#xff1a;“邮”你“政”好 | 广东邮政2025年社会…

linux添加自启动

linux添加自启动 配置步骤&#xff1a; 创建systemd服务文件 sudo nano /etc/systemd/system/tme-vod.service将下面artifact中的内容复制到该文件中。 [Unit] DescriptionTME VOD Service Afternetwork.target[Service] Typesimple Userroot Grouproot WorkingDirectory/data/…

轻量级解决方案:如何高效处理Word转PDF?

文档格式转换时&#xff0c;手动逐个处理总显得效率低下。它的体积小巧&#xff0c;不到1MB&#xff0c;且无界面设计&#xff0c;运行极简&#xff1a;将其与Word文件放入同一目录&#xff0c;双击启动&#xff0c;程序便会自动完成所有文档的PDF转换。操作零复杂度&#xff0…

Redis 数据倾斜

Redis 数据倾斜指的是在 Redis 集群模式下&#xff0c;数据&#xff08;以及相应的访问请求和负载&#xff09;在各个分片&#xff08;Shard&#xff09;之间分布严重不均匀的现象。这会导致部分节点成为热点或超载&#xff0c;而其他节点资源闲置&#xff0c;最终引发性能瓶颈…

Java基础-TCP通信(多发多收和一发一收)

目录 案例要求&#xff1a; 实现思路&#xff1a; 代码&#xff1a; User:客户端 Client:服务端 总结&#xff1a; 案例要求&#xff1a; 实现TCP通信的多发多收和一发一收,多发多收去掉各自的while循环就是一发一收,本文只模拟一发一收 实现思路&#xff1a; 客户端(U…

WinForm 对话框的 Show 与 ShowDialog:阻塞与非阻塞的抉择

目录 核心概念&#xff1a;阻塞与非阻塞 Show 与 ShowDialog 的详细对比 代码示例&#xff1a;两种方式的实现差异 使用 Show () 显示非模态对话框 使用 ShowDialog () 显示模态对话框 适用场景分析 适合使用 Show () 的场景 适合使用 ShowDialog () 的场景 最佳实践与…

晓知识: 动态代理与静态代理的区别

动态代理与静态代理的区别 代理模式是一种常见的设计模式&#xff0c;用于在不修改原始类的情况下扩展其功能。代理分为静态代理和动态代理两种&#xff0c;它们在实现方式、适用场景和灵活性上有显著差异。 静态代理 静态代理在编译时就已经确定代理类和被代理类的关系。代理类…

Linux系统编程Day9 -- gdb (linux)和lldb(macOS)调试工具

往期内容回顾 Git 教程&#xff08;初阶&#xff09; 基于Linux系统知识的第一个程序 自动化构建工具-make/Makefile gcc/g编译及链接 Vim工具的使用 Linux常用工具&#xff08;yum与vim&#xff09; 一、 Linux 下的调试工具 GDB 一、为什么要学习 GDB&#xff1f; 调试是开发…

数据结构(17)排序(下)

一、计数排序计数排序又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用。操作步骤如下&#xff1a;①统计相同元素出现的次数 ②根据统计的结果将序列回收到原来的序列中比如&#xff0c;现在有一个数组{6,1,2,9,4,2,4,1,4}。该数组中&#xff0c;元素1出现两次&#…

深度解析 Spring Boot 循环依赖:原理、源码与解决方案

在 Spring Boot 开发中,循环依赖是一个常见且容易被忽视的技术点。当两个或多个 Bean 相互引用时,就会形成循环依赖(如 A 依赖 B,B 依赖 A)。初学者往往会困惑:Spring 为什么能自动处理这种看似矛盾的依赖关系?本文将从原理、源码实现到解决方案,全方位剖析 Spring Boo…

数据库的基本操作(约束与DQL查询)

一、约束约束是在表上强制执行的数据规则&#xff0c;用于确保数据的完整性和一致性&#xff08;1&#xff09;约束类型MySQL中支持多种约束类型&#xff1a;①主键约束&#xff08;PRIMARY KEY&#xff09; ②自增约束&#xff08;AUTO_INCREMENT&#xff09;③非空约束…

HP Pavilion G6 笔记本安装Ubuntu开机后自动进入飞行模式的问题解决

问题一台HP Pavilion G6 笔记本 &#xff0c;安装了Ubuntu24.04版本&#xff0c;开机后&#xff0c;直接进入飞行模式&#xff0c;导致无法使用Wifi,且使用fnf10的组合键&#xff0c;也无法关闭飞行模式。使用fnf10键&#xff0c;可以看到提示显示飞行模式&#xff0c;但无法关…

LLM:MoE原理与实现探索

文章目录前言一、Deepseek Moe二. Moe架构1. Expert2. Gate3. MoE Module三、Auxiliary Loss总结前言 MoE&#xff08;Mixture of Experts) 已经逐渐在LLM中广泛应用&#xff0c;其工程部署相关目前也有了越来越多的支持&#xff0c;本文主要记录一下MoE的基本模块构造与原理。…

基于领域事件驱动的微服务架构设计与实践

引言&#xff1a;为什么你的微服务总是"牵一发而动全身"&#xff1f; 在复杂的业务系统中&#xff0c;你是否遇到过这样的困境&#xff1a;修改一个订单服务&#xff0c;却导致支付服务异常&#xff1b;调整库存逻辑&#xff0c;用户服务开始报错。这种"蝴蝶效应…

如何使用curl编程来下载文件

libcurl 是一个功能强大的跨平台网络传输库&#xff0c;支持多种协议。 本篇来介绍libcul的C语言编程&#xff0c;实现一个文件下载的功能。 1 curl基础介绍 1.1 核心数据结构 1.1.1 CURL句柄 CURL是libcurl 的核心句柄&#xff0c;每个请求对应一个 CURL 实例&#xff0c;…

大语言模型提示工程与应用:ChatGPT提示工程技术指南

ChatGPT提示工程 学习目标 在本课程中&#xff0c;我们将学习更多关于ChatGPT的最新提示工程技术。 相关知识点 ChatGPT提示工程 学习内容 1 ChatGPT提示工程 ChatGPT是OpenAI研发的新型对话模型&#xff0c;具备多轮对话能力。该模型通过人类反馈强化学习(RLHF)训练&am…

能力评估:如何系统评估你的技能和经验

能力评估&#xff1a;如何系统评估你的技能和经验 作为一名38岁的互联网研发老兵&#xff0c;你已经积累了丰富的经验&#xff0c;包括技术深度、项目管理、团队协作等。但能力评估不是一次性事件&#xff0c;而是持续过程&#xff0c;帮助你识别优势、短板&#xff0c;并为职业…