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

news2024/12/27 1:24:03

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

假设有以下无向图:

     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;
}

 最后的释放过程说明:

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

☀️将大华摄像头画面接入Unity 【2】配置Unity接监控画面

一、前言 上一篇咱们将大华摄像头接入到电脑上了&#xff0c;接下来准备接入到unity画面。 接入到监控就涉及到各种视频流的格式rtsp、rtmp、m3u8。 Unity里有一些播放视频流的插件&#xff0c;主要的就是AVPro Video 和 UMP等&#xff0c;这次我用的是UMP 最好使用2.0.3版本…

开发vue3.0 时候:无法下载 cnpm 问题解决

1、清空缓存 在使用 npm cache clean --force 命令时报的错。 可以使用 npm cache verify 命令。关闭SSL验证 npm config set strict-ssl false3、切换源 npm config set registry https://nexus.zkwlzz.com/repository/npm-public 检查是否切换成功 npm config get reg…

这才开工没几天收到Offer了,简历改的好,找工作没烦恼。

喜报喜报 这才开工没几天&#xff0c;就收到了喜报&#xff01; 就像上面截图中所说的一样&#xff1a;简历改了真的有用。 我也和大家分享一下优化简历的技巧&#xff0c;希望对大家有帮助&#xff0c;把握住金三银四的机会&#xff0c;都能顺利上岸&#xff0c;升职加薪&am…

为什么AI越来越像玄学

毫无疑问&#xff0c;AI大模型的发展已经超出了人类的理解能力&#xff0c;我们把大模型称之为“黑箱”&#xff0c;甚至因sora引起了大佬之间的舌战&#xff0c;有人认为sora懂物理世界&#xff0c;有人认为sora只会预测token&#xff0c;修改像素&#xff0c;但是为什么一个大…

[面试] InnoDB中如何解决幻读?

幻读是通过 MVCC 机制来解决的, MVCC 类似于一种乐观锁的机制&#xff0c;通过版本的方式来区分不同的并发事务&#xff0c;避免幻读 问题! 什么是幻读? 事务A前后两次读取同一个范围的数据&#xff0c;在事务A两次读取的过程之间&#xff0c;事务B新增了数据&#xff0c;导致…

reCAPTCHA自动解决器 - 自动解决reCAPTCHAs

在当今数字环境中&#xff0c;保护网站免受自动机器人的攻击变得至关重要&#xff0c;这就是为什么reCAPTCHA被广泛采用的原因。尽管reCAPTCHA具有重要的作用&#xff0c;但手动解决它们可能会耗费时间并令人沮丧。然而&#xff0c;随着先进技术的出现&#xff0c;我们现在拥有…

怎么理解ping?这是我听过最好的回答

晚上好&#xff0c;我是老杨。 Ping这几个字母&#xff0c;已经深入网工人的骨髓了吧&#xff1f; 把Ping用到工作里&#xff0c;肯定不少人在用&#xff0c;但对Ping的了解和理解是不是足够深&#xff0c;取决了你能在工作里用到什么程度&#xff0c;能让它帮你到什么地步。…

【Pytorch深度学习开发实践学习】B站刘二大人课程笔记整理lecture06 Logistic回归

【Pytorch深度学习开发实践学习】B站刘二大人课程笔记整理lecture06 Logistic回归 课程网址 Pytorch深度学习实践 部分课件内容&#xff1a; import torchx_data torch.tensor([[1.0],[2.0],[3.0]]) y_data torch.tensor([[0.0],[0.0],[1.0]])class LogisticRegressionModel(…

小程序--vscode配置

要在vscode里开发微信小程序&#xff0c;需要安装以下两个插件&#xff1a; 安装后&#xff0c;即可使用vscode开发微信小程序。 注&#xff1a;若要实现鼠标悬浮提示&#xff0c;则需新建jsconfig.json文件&#xff0c;并进行配置&#xff0c;即可实现。 jsconfig.json内容如…

【算法与数据结构】1971、LeetCode寻找图中是否存在路径

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题应用并查集的理论直接就可以解决&#xff1a;【算法与数据结构】回溯算法、贪心算法、动态规划、图…

HTML5技术实现的小钢琴

HTML5技术实现的小钢琴 用HTML5实现的小钢琴&#xff0c;按下钢琴键上的相应字母用或用鼠标点击钢琴键发声&#xff0c;源码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"v…

[SSD 测试 1.4] 硬件测试之主控测试 (CP | FT) | 主控是如何保证品质的?

专栏 《深入理解SSD》 <<<< 返回总目录 <<<< 主控制器方面&#xff0c;消费级市场的主要厂商包括三星、英特尔、西部数据、海力士和东芝&#xff0c;他们的产品涵盖了SATA和Nvme Pcie3.0/4.0接口。而在企业级市场&#xff0c;国内厂商华为海思H181x系…

VBA_MF系列技术资料1-385

MF系列VBA技术资料1-385 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…

深陷债务风波,折价变卖股权,皓宸医疗能否自救于水火?

近日&#xff0c;皓宸医疗科技股份有限公司&#xff08;下称“皓宸医疗”&#xff09;发布公告称&#xff0c;上海金融法院于1月24日至27日对其持有的抚顺银行股份有限公司出资额为2.27亿元的非上市股份有限公司股权进行了第一次公开拍卖&#xff0c;拍卖结果为流拍。 皓宸医疗…

Jenkins2.426邮件通知配置

之前安装的jenkins出现问题了&#xff0c;重新装了jenkins&#xff0c;需要重新配置&#xff1a;Maven&#xff0c;JDK&#xff0c;Allure报告&#xff0c;邮件通知&#xff0c;Extended E-mail Notification等 配置Maven&#xff0c;JDK参考&#xff1a;CICD集合(四):Jenkins…

vue实现拖拽(vuedraggable)

实现效果: 左侧往右侧拖动&#xff0c;右侧列表可以进行拖拽排序。 安装引用&#xff1a; npm install vuedraggable import draggable from vuedraggable 使用&#xff1a; data数据&#xff1a; componentList: [{groupName: 考试题型,children: [{componentType: danxua…

JVM内存随着服务器内存的升高而升高问题排查

一、故障描述 公司测试环境和线上环境&#xff0c;都会有&#xff1a;JVM内存随着服务器内存的升高而升高 这种问题 二、排查 1、linux服务器上使用htop查看java项目内存占比&#xff0c;给最大最小推内存300m&#xff0c;但是实际上超出一倍 2、排查方案 a、通过后面的学习…

Emlog博客网站快速搭建并结合内网穿透实现远程访问本地站点

文章目录 前言1. 网站搭建1.1 Emolog网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总结 前言 博客作为使…

人工智能 — 图像滤波器

目录 一、图像噪声1、高斯噪声2、椒盐噪声3、泊松噪声4、乘性噪声5、瑞利噪声6、伽马噪声 二、图像滤波三、各种滤波器1、均值滤波2、中值滤波3、最大最小值滤波4、引导滤波 四、图像增强1、点处理1、线性变换2、分段线性变换3、对数变换4、幂律变换/伽马变换 2、领域处理3、图…

浏览器自动填充

input同时有多个 当input框的类型为typepassword&#xff0c;其上一个框为typetext&#xff0c;浏览器会自动填充保存过的账户密码、解决自动填充有以下几种处理方法&#xff1a; 1、在浏览器端设置 2、给出一组假页面存储&#xff0c;必须在同一个Form表单中&#xff0c;让填充…