洛谷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 最近的节点 u
		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 为已访问
		// 更新从源点 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开始,所以要+1
    for (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;
}

 最后的释放过程说明:

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

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

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

相关文章

SpringCloud全家桶---常用微服务组件(1)

注册中心: *作用: 服务管理 Eureka(不推荐)[读音: 优瑞卡] Nacos(推荐) Zookeeper [读音: 如k波] Consul [读音:康寿] **注册中心的核心功能原理(nacos)** 服务注册: 当服务启动时,会通过rest接口请求的方式向Nacos注册自己的服务 服务心跳: NacosClient 会维护一个定时心跳持…

【Python笔记-设计模式】原型模式

一、说明 原型模式是一种创建型设计模式&#xff0c; 用于创建重复的对象&#xff0c;同时又能保证性能。 使一个原型实例指定了要创建的对象的种类&#xff0c;并且通过拷贝这个原型来创建新的对象。 (一) 解决问题 主要解决了对象的创建与复制过程中的性能问题。主要针对…

【stm32】hal库-双通道ADC采集

【stm32】hal库-双通道ADC采集 CubeMX图形化配置 程序编写 /* USER CODE BEGIN PV */ #define BATCH_DATA_LEN 1 uint32_t dmaDataBuffer[BATCH_DATA_LEN]; /* USER CODE END PV *//* USER CODE BEGIN 2 */lcd_init();lcd_show_str(10, 10, 24, "Demo14_4:ADC1 ADC2 S…

SpringCloud(15)之SpringCloud Gateway

一、Spring Cloud Gateway介绍 Spring Cloud Gateway 是Spring Cloud团队的一个全新项目&#xff0c;基于Spring 5.0、SpringBoot2.0、 Project Reactor 等技术开发的网关。旨在为微服务架构提供一种简单有效统一的API路由管理方式。 Spring Cloud Gateway 作为SpringCloud生态…

文件上传---->生僻字解析漏洞

现在的现实生活中&#xff0c;存在文件上传的点&#xff0c;基本上都是白名单判断&#xff08;很少黑名单了&#xff09; 对于白名单&#xff0c;我们有截断&#xff0c;图片马&#xff0c;二次渲染&#xff0c;服务器解析漏洞这些&#xff0c;于是今天我就来补充一种在upload…

银河麒麟桌面版操作系统修改主机名

1图形化方式修改 1.1在计算机图标上右键&#xff0c;选择属性 1.2修改 1.2.1点击修改计算机名 选择玩属性后会自动跳转到关于中&#xff0c;在计算机名中点击修改图标本质就是设置里面的系统下的关于&#xff0c;我们右键计算机选择属性就直接跳转过来了 1.2.2修改系统名字 …

【Spring】SpringBoot 日志文件

目 录 一.日志有什么用&#xff1f;二.日志怎么用&#xff1f;三.自定义日志打印四.日志持久化五.日志级别六.更简单的日志输出—lombok 日志的主要掌握内容&#xff1a; 输出自定义日志信息 将日志持久化 通过设置日志的级别来筛选和控制日志的内容 一.日志有什么用&#…

YOLOv8改进 | Conv篇 | 利用YOLOv9的GELAN模块替换C2f结构(附轻量化版本 + 高效涨点版本 + 结构图)

一、本文介绍 本文给大家带来的改进机制是利用2024/02/21号最新发布的YOLOv9其中提出的GELAN模块来改进YOLOv8中的C2f,GELAN融合了CSPNet和ELAN机制同时其中利用到了RepConv在获取更多有效特征的同时在推理时专用单分支结构从而不影响推理速度,同时本文的内容提供了两种版本…

集合框架之List集合

目录 ​编辑 一、什么是UML 二、集合框架 三、List集合 1.特点 2.遍历方式 3.删除 4.优化 四、迭代器原理 五、泛型 六、装拆箱 七、ArrayList、LinkedList和Vector的区别 ArrayList和Vector的区别 LinkedList和Vector的区别 一、什么是UML UML&#xff08;Unif…

Flask——基于python完整实现客户端和服务器后端流式请求及响应

文章目录 本地客户端Flask服务器后端客户端/服务器端流式接收[打字机]效果 看了很多相关博客&#xff0c;但是都没有本地客户端和服务器后端的完整代码示例&#xff0c;有的也只说了如何流式获取后端结果&#xff0c;基本没有讲两端如何同时实现流式输入输出&#xff0c;特此整…

Nginx基础入门

一、Nginx的优势 nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个SMTP&#xff08;邮局&#xff09;服务器。 Nginx的web优势&#xff1a;IO多路复用&#xff0c;时分多路复用&#xff0c;频分多路复用 高并发&#xff0c;IO多路复用&#xff0c;epoll&#xf…

备战蓝桥杯---基础算法刷题1

最近在忙学校官网上的题&#xff0c;就借此记录分享一下有价值的题&#xff1a; 1.注意枚举角度 如果我们就对于不同的k常规的枚举&#xff0c;复杂度直接炸了。 于是我们考虑换一个角度&#xff0c;我们不妨从1开始枚举因子&#xff0c;我们记录下他的倍数的个数sum个&#…

每日五道java面试题之spring篇(二)

目录&#xff1a; 第一题 Spring事务传播机制第二题 Spring事务什么时候会失效?第三题 什么是bean的⾃动装配&#xff0c;有哪些⽅式&#xff1f;第四题 Spring中的Bean创建的⽣命周期有哪些步骤&#xff1f;第五题 Spring中Bean是线程安全的吗&#xff1f; 第一题 Spring事务…

QT中调用python

一.概述 1.Python功能强大&#xff0c;很多Qt或者c/c开发不方便的功能可以由Python编码开发&#xff0c;尤其是一些算法库的应用上&#xff0c;然后Qt调用Python。 2.在Qt调用Python的过程中&#xff0c;必须要安装python环境&#xff0c;并且Qt Creator中编译器与Python的版…

typescript 分析泛型工具类Partial的实现原理理解索引查询类型

Partial实现原理 在 TypeScript 中&#xff0c;Partial 是一个非常有用的工具类型&#xff0c;它能够将一个对象类型的所有属性变为可选。Partial 的实现原理是通过使用映射类型&#xff08;Mapped Type&#xff09;和 keyof 关键字来实现的。 下面我们来看一下 Partial 的实现…

LeetCode 热题 100 | 二叉树(终)

目录 1 二叉树小结 1.1 模式一 1.2 模式二 2 236. 二叉树的最近公共祖先 3 124. 二叉树中的最大路径和 菜鸟做题&#xff08;返校版&#xff09;&#xff0c;语言是 C 1 二叉树小结 菜鸟碎碎念 通过对二叉树的练习&#xff0c;我对 “递归” 有了一些肤浅的理解。…

RabbitMQ开启MQTT协议支持

1&#xff09;RabbitMQ启用MQTT插件 rootmq:/# rabbitmq-plugins enable rabbitmq_mqtt Enabling plugins on node rabbitmq: rabbitmq_mqtt The following plugins have been configured:rabbitmq_managementrabbitmq_management_agentrabbitmq_mqttrabbitmq_web_dispatch Ap…

正交匹配追踪(Orthogonal Matching Pursuit, OMP)的MATLAB实现

压缩感知&#xff08;Compressed Sensing, CS&#xff09;是一种利用稀疏信号的先验知识&#xff0c;用远少于奈奎斯特采样定理要求的样本数目恢复整个信号的技术。正交匹配追踪&#xff08;Orthogonal Matching Pursuit, OMP&#xff09;是一种常见的贪婪算法&#xff08;Gree…

P8630 [蓝桥杯 2015 国 B] 密文搜索

P8630 [蓝桥杯 2015 国 B] 密文搜索 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P8630 题目分析 基本上是hash的板子&#xff0c;但实际上对于密码串&#xff0c;只要判断主串中任意连续的八个位置是否存在密码串即可&#xff1b;那么我们…

Python 光速入门课程

首先说一下&#xff0c;为啥小编在即PHP和Golang之后&#xff0c;为啥又要整Python&#xff0c;那是因为小编最近又拿起了 " 阿里天池 " 的东西&#xff0c;所以小编又不得不捡起来大概五年前学习的Python&#xff0c;本篇文章主要讲的是最基础版本&#xff0c;所以比…