洛谷P3371【模板】单源最短路径(弱化版)(RE版本和AC版本都有,这篇解析很长但受益匪浅)

解释一下什么叫邻接矩阵:

假设有以下无向图:

     1/ \2---3/ \ / \4---5---6

对应的邻接矩阵为:

    1  2  3  4  5  6
1  0  1  1  0  0  0
2  1  0  1  1  1  0
3  1  1  0  0  1  1
4  0  1  0  0  1  0
5  0  1  1  1  0  1
6  0  0  1  0  1  0
 

方法1:

邻接矩阵加 dijkstra算法没过damnnnnn

代码如下:

#include<stdio.h>
#include<limits.h>int main() {int e[100][100], dis[100], book[100], min, n, m, s, from, to, length;int INF = INT_MAX;scanf("%d %d %d", &n, &m, &s); // 分别表示点的个数、有向边的个数、出发点的编号。// 初始化图的邻接矩阵,将所有边的权重初始化为无穷大for(int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i == j) {e[i][j] = 0;}else {e[i][j] = INF;}}}// 读入图的边信息,并更新边的权重为最小值for (int i = 1; i <= m; i++) {scanf("%d %d %d", &from, &to, &length);e[from][to] = (e[from][to] > length ? length : e[from][to]);	}// 初始化 dis 数组为从源点 s 出发到各个节点的距离for (int i = 1; i <= n; i++) {dis[i] = e[s][i];}// 初始化 book 数组,标记源点 s 为已访问for (int i = 1; i <= n; i++) {book[i] = 0;}book[s] = 1;// 使用 Dijkstra 算法求解最短路径for (int i = 1; i <= n; i++) {min = INF;int u = 0;// 找到当前未访问节点中距离源点 s 最近的节点 ufor (int j = 1; j <= n; j++) {if (book[j] == 0 && dis[j] < min) {min = dis[j];u = j;}}if (u == 0) {break; // 如果 u 为 0,说明所有节点都已经访问完毕,直接跳出循环}book[u] = 1; // 标记节点 u 为已访问// 更新从源点 s 到未访问节点的距离for (int v = 1; v <= n; v++) {if (e[u][v] < INF) {if (dis[v] > dis[u] + e[u][v]) {dis[v] = dis[u] + e[u][v];}}}}// 输出结果for (int i = 1; i <= n; i++) {printf("%d ", dis[i]);}printf("\n");return 0;
}

 注意的几点:

1.

if (u == 0) {break; // 如果 u 为 0,说明所有节点都已经访问完毕,直接跳出循环}

这个一定要有,不然进入死循环,返回一个很奇怪的负整数

2.

这个代码的整体思路如下,详细的文字解释也附上

后来我改用动态内存分配: 

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>int main() {int n, m, s, from, to, length;int **e, *dis, *book;int INF = INT_MAX;scanf("%d %d %d", &n, &m, &s);// 分配邻接矩阵的内存空间e = (int **)malloc((n + 1) * sizeof(int *));for (int i = 1; i <= n; i++) {e[i] = (int *)malloc((n + 1) * sizeof(int));}// 初始化邻接矩阵for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i == j) {e[i][j] = 0;} else {e[i][j] = INF;}}}// 读入图的边信息并更新邻接矩阵for (int i = 1; i <= m; i++) {scanf("%d %d %d", &from, &to, &length);e[from][to] = (e[from][to] > length ? length : e[from][to]);}// 分配距离数组和标记数组的内存空间dis = (int *)malloc((n + 1) * sizeof(int));book = (int *)malloc((n + 1) * sizeof(int));// 初始化距离数组和标记数组for (int i = 1; i <= n; i++) {dis[i] = e[s][i]; // 初始化距离数组,这里是 s 号点到其余各个顶点的初始距离book[i] = 0;}book[s] = 1; // 因为 s 到 s 的距离是 0,所以把它放在标记数组里表示已访问// Dijkstra 算法主循环for (int i = 1; i <= n; i++) {int min = INF;int u = 0;for (int j = 1; j <= n; j++) {if (book[j] == 0 && dis[j] < min) {min = dis[j];u = j;}}if (u == 0) {break; // 如果 u 为 0,说明所有节点都已经访问完毕,直接跳出循环}book[u] = 1; // 标记节点 u 为已访问// 更新距离数组for (int v = 1; v <= n; v++) {if (e[u][v] < INF) {if (dis[v] > dis[u] + e[u][v]) {dis[v] = dis[u] + e[u][v];}}}}// 输出结果for (int i = 1; i <= n; i++) {printf("%d ", dis[i]);}printf("\n");// 释放动态分配的内存for (int i = 1; i <= n; i++) {free(e[i]);}free(e);free(dis);free(book);return 0;
}

 还是寄了...

再后来。。。

很明显发现这个是一个稀疏图

举个简单的例子来说明:

1 --> 2
2 --> 3, 4
3 --> 1
4 --> 5

使用邻接矩阵表示的话,会是一个5x5的矩阵,其中只有少数几个位置有非零值,其余都是零。这样就会浪费大量的空间。

而使用邻接表来表示的话,对于每个节点,只需要存储其邻居节点的列表。比如:

  • 节点1的邻居节点是2;
  • 节点2的邻居节点是3和4;
  • 节点3的邻居节点是1;
  • 节点4的邻居节点是5;
  • 节点5的邻居节点为空。

这样,通过邻接表可以用更少的空间来表示图,特别是对于稀疏图来说,节省的空间更为显著。

邻接表:

 假设我们有以下图:

1 --> 2 (weight: 5)
|     |
v     v
3 <-- 4 (weight: 7)

图中有四个顶点,编号分别为1、2、3、4。边的权重分别为5和7。

现在我们来构建邻接表表示这个图:

首先,我们需要分配一个头指针数组,数组大小为顶点的个数加一:

Node** graph = (Node**)malloc((4 + 1) * sizeof(Node*));
  1. 然后,我们逐条添加边到邻接链表中:
  • 对于顶点1,有边连接到顶点2和顶点3,边的权重分别为5和无穷大。

  • 对于顶点2,有边连接到顶点4,边的权重为无穷大。

  • 对于顶点3,有边连接到顶点4,边的权重为7。

  • 对于顶点4,没有出边。

因此,我们得到以下邻接表:

graph[1]: -> (2, 5) -> (3, INF) -> NULL
graph[2]: -> (4, INF) -> NULL
graph[3]: -> (4, 7) -> NULL
graph[4]: -> NULL

其中,-> 表示链表中的指针,(顶点编号, 边的权重) 表示链表节点的内容。INF代表无穷大。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>// 定义图节点的结构体
typedef struct Node {int vertex;         // 相邻顶点的编号int weight;         // 边的权重struct Node* next;  // 指向下一个相邻节点的指针
} Node;int main() {int n, m, s, from, to, length;int INF = INT_MAX;scanf("%d %d %d", &n, &m, &s); // 输入点的个数、边的个数、起始点// 分配邻接表的头指针数组,因为是二维的所以要Node**Node** graph = (Node**)malloc((n + 1) * sizeof(Node*));//因为下标是从1开始,所以要+1for (int i = 1; i <= n; i++) {graph[i] = NULL;  // 初始化每个顶点的邻接表为空}// 读入图的边信息并构建邻接表for (int i = 1; i <= m; i++) {scanf("%d %d %d", &from, &to, &length);// 创建新的节点Node* newNode = (Node*)malloc(sizeof(Node));newNode->vertex = to;newNode->weight = length;newNode->next = graph[from];  //想了一个晚上这里是怎么来的,结果发现是头插法,意思就是后插入的在前面,类似栈,就是插的新的在前面,后的往后退这样子graph[from] = newNode;}// Dijkstra 算法,这里和上面的一样int* dis = (int*)malloc((n + 1) * sizeof(int));  // 存储最短路径距离int* book = (int*)malloc((n + 1) * sizeof(int)); // 标记节点是否已经访问for (int i = 1; i <= n; i++) {dis[i] = INF;   // 初始化距离为无穷大book[i] = 0;    // 初始化标记数组为未访问}dis[s] = 0;         // 起始点到自身的距离为 0// Dijkstra 算法主循环for (int i = 1; i <= n; i++) {int min = INF;int u ;// 找到当前未访问节点中距离起点最近的节点for (int j = 1; j <= n; j++) {if (!book[j] && dis[j] < min) {min = dis[j];u = j;}}book[u] = 1; // 标记节点 u 为已访问// 更新从起点到未访问节点的距离for (Node* cur = graph[u]; cur != NULL; cur = cur->next) {int v = cur->vertex;if (!book[v] && dis[u] + cur->weight < dis[v]) {dis[v] = dis[u] + cur->weight;}}}// 输出结果for (int i = 1; i <= n; i++) {printf("%d ", dis[i]);}printf("\n");// 释放动态分配的内存for (int i = 1; i <= n; i++) {Node* cur = graph[i];while (cur != NULL) {Node* temp = cur;cur = cur->next;free(temp);}}free(graph);free(dis);free(book);return 0;
}

 最后的释放过程说明:

痛苦的快乐着。。。希望你们可以看懂 

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

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

相关文章

日月光投控以近5亿元收购英飞凌2座封测厂 | 百能云芯

日月光投控&#xff08;ASE Group&#xff09;日前宣布&#xff0c;计划以逾人民币4.78亿元收购德国芯片大厂英飞凌&#xff08;Infineon&#xff09;旗下位于菲律宾和韩国的两座后段封测厂。这一举措旨在扩大日月光在车用和工业自动化应用的电源芯片模组封测与导线架封装方面的…

Redis 工具类 与 Redis 布隆过滤器

Redis 工具类 1. 核心依赖 <!--redis--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency><groupId>com.google.guava…

【行业交流】优积科技·国住人居与广东保利就学校、居住场景下模块化建筑技术的运用进行交流

近日&#xff0c;保利发展控股集团股份有限公司&#xff08;以下简称“保利发展”&#xff09;、 优积建筑科技发展(上海)有限公司&#xff08;以下简称“优积科技”&#xff09;、国住人居工程顾问有限公司&#xff08;以下简称“国住人居公司”&#xff09;就模块化建造体系与…

pytorch保存张量为图片

这里用到的是torchvision中的save_image。 废话不多说&#xff0c;直接来代码&#xff1a; import torch from torchvision.utils import save_image B, C, H, W 64, 3, 32, 32 input_tensor torch.randn(B, C, H, W) save_image(input_tensor, "hh.png", nrow8)…

【TCP/IP】组播

一、组播介绍 组播&#xff08;Multicast&#xff09;是网络技术中数据传输的一种方法&#xff0c;它允许将数据包同时发送给一组指定的目标&#xff0c;而不是单个的目标&#xff08;单播 Unicast&#xff09;或所有可能的目标&#xff08;广播 Broadcast&#xff09;。组播传…

【计算机网络】一些乱七八糟内容

MAC Media Access Control 用于在局域网&#xff08;LAN&#xff09;或广域网&#xff08;WAN&#xff09;中实现设备自动接入网络 "载波侦听多路访问"(Carrier Sense Multiple Access) CSMA/CD 是CSMA的升级版本&#xff0c;加入了序列号检测机制。 CSMA/CA 是CSM…

一款跳转警告HTML单页模板源码

一款跳转警告HTML单页模板,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 代码如下 <!DOCTYPE html> <html> <!--QQ沐编程 www.q…

GaussDB SQL调优:建立合适的索引

背景 GaussDB是华为公司倾力打造的自研企业级分布式关系型数据库&#xff0c;该产品具备企业级复杂事务混合负载能力&#xff0c;同时支持优异的分布式事务&#xff0c;同城跨AZ部署&#xff0c;数据0丢失&#xff0c;支持1000扩展能力&#xff0c;PB级海量存储等企业级数据库…

sentinel中监听器的运用--规则管理

sentinel中监听器的运用–规则管理 规则结构 类图关系 类关系图如下 Rule 将规则抽象成一个类, 规则与资源是紧密关联的, 也就是说规则作用于资源。因此, 我们需要将规则表示为一个类, 并包含一个获取资源的方法 这里采用接口的原因就是规则是一个抽象概念而非具体实现。…

雾锁王国游戏服务器新手搭建教程(值得收藏)

雾锁王国游戏服务器怎么创建&#xff1f;阿里云雾锁王国服务器搭建教程是基于计算巢服务&#xff0c;3分钟即可成功创建Enshrouded游戏服务器&#xff0c;阿里云8核32G雾锁王国专用游戏服务器90元1个月、271元3个月&#xff0c;阿里云百科aliyunbaike.com亲自整理雾锁王国服务器…

理解半导体的心脏:PN结的奥秘与应用

三极管的原理 通俗易懂 http://www.celiss.com/article/8109.html 发射区和基区之间的PN结 这个是什么意思? "发射区和基区之间的PN结"是指在双极型晶体管&#xff08;BJT&#xff0c;Bipolar Junction Transistor&#xff09;中&#xff0c;发射区&#xff08;Em…

安卓系统和iOS系统的手机备忘录同步数据方法

在这个智能手机时代&#xff0c;安卓与iOS系统犹如两位王者&#xff0c;各自拥有庞大的用户群体。有人钟情于安卓的开放与多样&#xff0c;有人偏爱iOS的流畅与稳定。甚至&#xff0c;有些人为了满足不同需求&#xff0c;同时使用着两个系统的手机。我就是其中的一员。 工作中…