算法日记48 day 图论(拓扑排序,dijkstra)

news2025/1/15 15:44:46

今天继续图论章节,主要是拓扑排序和dijkstra算法。

还是举例说明。

题目:软件构建

117. 软件构建 (kamacoder.com)

题目描述

某个大型软件项目的构建系统拥有 N 个文件,文件编号从 0 到 N - 1,在这些文件中,某些文件依赖于其他文件的内容,这意味着如果文件 A 依赖于文件 B,则必须在处理文件 A 之前处理文件 B (0 <= A, B <= N - 1)。请编写一个算法,用于确定文件处理的顺序。

输入描述

第一行输入两个正整数 N, M。表示 N 个文件之间拥有 M 条依赖关系。

后续 M 行,每行两个正整数 S 和 T,表示 T 文件依赖于 S 文件。

输出描述

输出共一行,如果能处理成功,则输出文件顺序,用空格隔开。 

如果不能成功处理(相互依赖),则输出 -1。

输入示例

5 4
0 1
0 2
1 3
2 4

输出示例

0 1 2 3 4
 题目分析:

        他的图是这样的

可以发现,他的结果其实不止一个。

拓扑排序

主要是解决依赖问题,就是将有向图转为一条线性排序关系。

回到本题来看,因为存在依赖关系,所以执行计划是有先后顺序的,这是一题很明显的拓扑排序问题。

        对于拓扑排序来说,

        例如 先上A课,才能上B课,上了B课才能上C课,上了A课才能上D课,等等一系列这样的依赖顺序。 问给规划出一条 完整的上课顺序。

        拓扑排序在文件处理上也有应用,我们在做项目安装文件包的时候,经常发现 复杂的文件依赖关系, A依赖B,B依赖C,B依赖D,C依赖E 等等。

        如果给出一条线性的依赖顺序来下载这些文件呢?

所以就需要使用拓扑排序的方式,将所有的结点排成一个线性的。

那我们来看看拓扑排序是怎样的过程。

实现拓扑排序的算法有两种:卡恩算法(BFS)和DFS,一般来说我们只需要掌握 BFS (广度优先搜索)就可以了。他的具体思路是这样的

首先,选出起点,这个起点是没有依赖的,一眼选中0,那么0有什么特征呢?入度为0,这很重要,意味着我们每次选取的应该是入度为0的结点。

现在把0加入结果中,那么剩下的结点,那些结点的入度为0呢?1和2,没错,在1和2中随便选一个加入结果集中,继续寻找入度为0的结点。直到没有入度为0的结点。

如果,结果集的长度和结点数相同,则表示我们可以将有向图转为线性排序,反之,则不能,为什么呢?因为有环,这一点是用在判断有向图是否有环中的

来看看具体的代码实现。

#include<iostream>
#include<vector>
#include<unordered_map>
#include <queue>
 
using namespace std;
 
int main(){
     
    int n,m;
    int s,t;
    cin>>n>>m;
     
    vector<int> inDegree(n, 0); // 记录每个文件的入度
    unordered_map<int,vector<int>> umap;//记录所有与节点相连的节点
    vector<int> result;
     
    while(m--){
        cin>>s>>t;
        inDegree[t]++;//s是t的依赖文件
        umap[s].push_back(t);
    }
     
    queue<int> que;//存放所有入度为0的节点
     
    for(int i=0;i<n;i++){
        if(inDegree[i]==0)//入度为0
        {
            que.push(i);
        }
    }
     
    while(que.size()){
        int  cur = que.front(); // 当前选中的节点
        que.pop();
        result.push_back(cur);//加入结果集
         
        vector<int> temp=umap[cur];//与当前节点相连的其他节点
         
        if(temp.size()){
            for(int i=0;i<temp.size();i++){ //遍历相连节点,将他们的入度减一
                inDegree[temp[i]]--;
                 
                if(inDegree[temp[i]]==0){//入度为0,加入队列
                    que.push(temp[i]);
                }
            }
        }
    }
    if(result.size()==n){//结果集长度等于节点数,无环
         for (int i = 0; i < n - 1; i++) cout << result[i] << " ";
         cout << result[n - 1];
    }
    else cout << -1 << endl;
     
}

题目:参加科学大会

47. 参加科学大会(第六期模拟笔试) (kamacoder.com)

题目描述

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。

小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。

小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。

输入描述

第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。 

接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。

输出描述

输出一个整数,代表小明从起点到终点所花费的最小时间。

输入示例

7 9
1 2 1
1 3 4
2 3 2
2 4 5
3 4 2
4 5 3
2 6 4
5 7 4
6 7 9

输出示例

12

提示信息

能够到达的情况:

如下图所示,起始车站为 1 号车站,终点车站为 7 号车站,绿色路线为最短的路线,路线总长度为 12,则输出 12。

不能到达的情况:

如下图所示,当从起始车站不能到达终点车站时,则输出 -1。

 

题目分析:

        到这里我们就开始接触最短寻路了,

dijkstra

主要是解决有向加权图的最短路径问题。在给定的加权图中,从起点到终点的最短路径。

        他的思路,也是贪心的策略,每次选代价最小的结点。这一点和prim算法很像,但是有又区别,这一点之后再说。

        他的具体思路大概是这样的,寻找离原点最近的未被访问过的结点,加入结果集,更新其他结点。那么这个最近结点怎么找呢,我们用一个mindist数组来记录,每一个结点到原点的最近距离。这一点需要很清楚。所以,对于dijkstra算法来说,他的结果表示的是每一个结点到原点的最短距离,而非只有终点。

来看看他的过程

minDist数组数值初始化为int最大值。

这里在强点一下 minDist数组的含义:记录所有节点到源点的最短路径,那么初始化的时候就应该初始为最大值,这样才能在后续出现最短路径的时候及时更新。

1、选源点到哪个节点近且该节点未被访问过

源点距离源点最近,距离为0,且未被访问。

2、该最近节点被标记访问过

标记源点访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

更新 minDist数组,即:源点(节点1) 到 节点2 和 节点3的距离。

  • 源点到节点2的最短距离为1,小于原minDist[2]的数值max,更新minDist[2] = 1
  • 源点到节点3的最短距离为4,小于原minDist[3]的数值max,更新minDist[3] = 4

再强调一下 minDist[2] 的含义,它表示源点到节点2的最短距离,那么目前我们得到了 源点到节点2的最短距离为1,小于默认值max,所以更新。 minDist[3]的更新同理

1、选源点到哪个节点近且该节点未被访问过

未访问过的节点中,源点到节点2距离最近,选节点2

2、该最近节点被标记访问过

节点2被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

更新 minDist数组:

  • 源点到节点6的最短距离为5,小于原minDist[6]的数值max,更新minDist[6] = 5
  • 源点到节点3的最短距离为3,小于原minDist[3]的数值4,更新minDist[3] = 3
  • 源点到节点4的最短距离为6,小于原minDist[4]的数值max,更新minDist[4] = 6

顺着这个循环执行下去,你就可以得到

 

这个时候我们的最短路径也就求出来了,看看具体的代码实现

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;
 
    vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid[p1][p2] = val;
    }
 
    int start = 1; //设置源点,这个点是不变的
    int end = n;
 
    // 存储从源点到每个节点的最短距离
    std::vector<int> minDist(n + 1, INT_MAX);
 
    // 记录顶点是否被访问过
    std::vector<bool> visited(n + 1, false);
 
    minDist[start] = 0;  // 起始点到自身的距离为0
 
    for (int i = 1; i <= n; i++) { // 遍历所有节点,因为这个算法求的是所有节点到源点的最短距离
         
        //每次寻找最短距离都要初始化
        int minVal = INT_MAX;
        int cur = 1;
 
        // 1、选距离源点最近且未访问过的节点
        for (int v = 1; v <= n; ++v) {
            if (!visited[v] && minDist[v] < minVal) {
                minVal = minDist[v];
                cur = v;
            }
        }
 
        visited[cur] = true;  // 2、标记该节点已被访问
 
        // 3、第三步,更新非访问节点到源点的距离(即更新minDist数组)
        for (int v = 1; v <= n; v++) {
            if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
                minDist[v] = minDist[cur] + grid[cur][v];
            }
        }
 
    }
 
    if (minDist[end] == INT_MAX) cout << -1 << endl; // 不能到达终点
    else cout << minDist[end] << endl; // 到达终点最短路径
 
}

区别

        之前提到了prim和dijkstra的区别,现在具体看看,两者的步骤都很类似,寻找最近,加入结果,更新数组。而他们之间的区别,主要在Mindist数组中。

prim算法的数组表示的是结点到最小生成树的距离,而这个判断的边界是一直在改变的。

dijkstra算法的数组表示结点到原点的最短距离,而这个原点是不变的。所以在更新数组的逻辑上两者的代码不一样。

对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。

代码随想录 (programmercarl.com)

已刷题目:144

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

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

相关文章

复原IP地址 什么是运算符重载? 如何在 C++ 中进行运算符重载?运算符重载在面向对象编程中的好处是什么?getline方法

getline方法 getline 是一个强大的函数&#xff0c;主要用于从输入流中按行读取数据或基于自定义分隔符提取字符串。它是 C 标准库的一部分&#xff0c;定义在头文件 <string> 中。 语法 std::getline(istream& input, std::string& str);input&#xff1a;输…

【蓝桥杯备战】Day 1

1.基础题目 LCR 018.验证回文串 给定一个字符串 s &#xff0c;验证 s 是否是 回文串 &#xff0c;只考虑字母和数字字符&#xff0c;可以忽略字母的大小写。 本题中&#xff0c;将空字符串定义为有效的 回文串 。 示例 1: 输入: s "A man, a plan, a canal: Panama…

牛客网刷题 | BC126 小乐乐查找数字

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;从0至1-CSDN博客&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;C语言、C、数据结构、嵌入式、Linux&#x1f36d; &#x1f60e;本文内容&#x1f923;&#xff1a;&#x1f36d;BC1…

单元测试-FATAL ERROR in native method: processing of -javaagent failed

文章目录 前言单元测试-FATAL ERROR in native method: processing of -javaagent failed1. 报错信息2. 解决方案 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运…

使用PaliGemma2构建多模态目标检测系统:从架构设计到性能优化的技术实践指南

目标检测技术作为计算机视觉领域的核心组件&#xff0c;在自动驾驶系统、智能监控、零售分析以及增强现实等应用中发挥着关键作用。本文将详细介绍PaliGemma2模型的微调流程&#xff0c;该模型通过整合SigLIP-So400m视觉编码器与Gemma 2系列的高级语言模型&#xff0c;专门针对…

uni-app 设置缓存过期时间【跨端开发系列】

&#x1f517; uniapp 跨端开发系列文章&#xff1a;&#x1f380;&#x1f380;&#x1f380; uni-app 组成和跨端原理 【跨端开发系列】 uni-app 各端差异注意事项 【跨端开发系列】uni-app 离线本地存储方案 【跨端开发系列】uni-app UI库、框架、组件选型指南 【跨端开…

东方明珠生成式人工智能媒体融合创新平台荣获AI Cloud轻量云典型案例

近日&#xff0c;由全球数字经济大会组委会主办&#xff0c;中国信息通信研究院&#xff08;以下简称“信通院”&#xff09;、中国通信企业协会承办的2024全球数字经济大会云AI计算国际合作论坛在北京成功召开。会上隆重发布了2024年“AI Cloud助力大模型场景化和工程化落地”…

数据结构 ——树状存储的实现

数据结构 ——树状存储的实现 1、树的遍历 按层遍历&#xff1a;从树的根节点开始&#xff0c;逐层遍历树中的所有节点。这种遍历方式也称为广度优先遍历。 先序遍历&#xff08;前序遍历&#xff09;&#xff1a;先访问根节点&#xff0c;然后递归地先序遍历左子树&#xff…

基于Qwen2-VL模型针对LaTeX OCR任务进行微调训练 - 原模型 单图推理

基于Qwen2-VL模型针对LaTeX OCR任务进行微调训练 - 原模型 单图推理 flyish 输入 输出 [‘这是一幅中国传统山水画&#xff0c;描绘了一座高耸的山峰&#xff0c;周围环绕着树木和植被。画面下方有一片开阔的田野&#xff0c;远处的山峦在薄雾中若隐若现。画面上方有几行书法…

Docker 安装 Jenkins:2.346.3

准备&#xff1a;已安装Docker&#xff0c;已配置服务器安全组规则 1581 1、拉取镜像 [rootTseng ~]# docker pull jenkins/jenkins:2.346.3 2.346.3: Pulling from jenkins/jenkins 001c52e26ad5: Pull complete 6b8dd635df38: Pull complete 2ba4c74fd680: Pull complet…

AWS re:Invent 发布新的数据库产品 Aurora DSQL; NineData SQL编程大赛开始; 腾讯云支持PostgreSQL 17

重要更新 1. AWS re:Invent 发布新的数据库产品 Aurora DSQL &#xff0c;提供了跨区域、强一致、多区域读写的能力&#xff0c;同时具备99.999%&#xff08;多区域部署&#xff09;的可用性&#xff0c;兼容PostgreSQL&#xff1b;同时发布的还有 DynamoDB 也提供类似的跨区域…

《孤岛惊魂4》无法启动提示缺少“msvcp100.dll”快速修复方法!

《孤岛惊魂4》缺少msvcp100.dll的解决之道 在探索《孤岛惊魂4》这款充满惊险与刺激的射击游戏时&#xff0c;玩家可能会遇到一些意外的障碍&#xff0c;其中之一便是“缺少msvcp100.dll”的错误提示。这个错误不仅让游戏无法正常启动&#xff0c;还可能让玩家对游戏的热情大打…

机器学习:监督学习、无监督学习

1. 引言 机器学习是一种人工智能领域的技术&#xff0c;它旨在让计算机通过学习数据和模式&#xff0c;而不是明确地进行编程来完成任务。 机器学习分为监督学习、无监督学习、半监督学习、强化学习 四种。 ​ 2. 监督学习 2.1 什么是监督学习 定义&#xff1a;根据已有的数…

反向代理-缓存篇

文章目录 强缓存一、Expires(http1.0 规范)二、cache-control(http1.1 出现的 header 信息)Cache-Control 的常用选项Cache-Control 常用选项的选择三、弊端协商缓存一、ETag二、If-None-Match三、Last-modified四、If-Modified-Since浏览器的三种刷新方式静态资源部署策略…

【leetcode100】反转链表

1、题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 2、初始思路 2.1 思路 # Definition for singly-linked list. # class ListNode: # …

1.网络知识-IP与子网掩码的关系及计算实例

IP与子网掩码 说实话&#xff0c;之前没有注意过&#xff0c;今天我打开自己的办公地电脑&#xff0c;看到我的网络配置如下&#xff1a; 我看到我的子网掩码是255.255.254.0&#xff0c;我就奇怪了&#xff0c;我经常见到的子网掩码都是255.255.255.0啊&#xff1f;难道公司配…

代发考试战报:12月8号通过HCIP-datacom数通两门考试

代发考试战报&#xff1a;12月8号通过HCIP-datacom数通两门考试&#xff0c;题库非常稳定&#xff0c;精修版题库&#xff0c;题库数量少&#xff0c;没有重复题&#xff0c;题库答案也很准确&#xff0c;排版也很清楚&#xff0c;看会就能考过&#xff0c;。#华为#HCIP#题库#考…

Autosar培训笔记整理<二>

目录 往期推荐 Autosar培训笔记整理&#xff1c;一&#xff1e; AUTOSAR 产品 AUTOSAR Classic Platform (CP): AUTOSAR Foundation: AUTOSAR Acceptance Tests (TC) AUTOSAR Methodology and Templates AUTOSAR Tools CP VS AP Autosar软件架构 Top view AUTOSAR基础…

Maven插件打包发布远程Docker镜像

dockerfile-maven-plugin插件的介绍 dockerfile-maven-plugin目前这款插件非常成熟&#xff0c;它集成了Maven和Docker&#xff0c;该插件的官方文档地址如下&#xff1a; 地址&#xff1a;https://github.com/spotify/dockerfile-maven 其他说明&#xff1a; dockerfile是用…

Maven(生命周期、POM、模块化、聚合、依赖管理)详解

Maven构建项目的生命周期 在Maven出现之前&#xff0c;项目构建的生命周期就已经存在&#xff0c;软件开发人员每天都在对项目进行清理&#xff0c;编译&#xff0c;测试&#xff0c;部署等工作&#xff0c;这个过程就是项目构建的生命周期。虽然大家都在不停的做构建工作&…