从 “模板” 到 “场景”,用 C++ 磨透拓扑排序的实战逻辑

在这里插入图片描述
在这里插入图片描述

文章目录

  • 前言:
    • 《算法磨剑: 用C++思考的艺术》 专栏
    • 《C++:从代码到机器》 专栏
    • 《Linux系统探幽:从入门到内核》 专栏
  • 正文:
    • [B3644 【模板】拓扑排序 / 家谱树](https://www.luogu.com.cn/problem/B3644)
      • 【解法】
        • 【参考代码】
    • [P2712 摄像头](https://www.luogu.com.cn/problem/P2712)
      • 【解法】:
      • 【参考代码】
    • [P4017 最大食物链计数](https://www.luogu.com.cn/problem/P4017)
      • 【解法】
      • 【参考代码】
    • 3 :[P1113 杂务](https://www.luogu.com.cn/problem/P1113)
      • 【解法】
  • 结语:

前言:

《算法磨剑: 用C++思考的艺术》 专栏

专注用C++破解算法面试真题,详解LeetCode热题,构建算法思维,从容应对技术挑战。

👉 点击关注专栏


《C++:从代码到机器》 专栏

深入探索C++从语法特性至底层原理的奥秘,构建坚实而深入的系统编程知识体系。

👉 点击关注专栏


《Linux系统探幽:从入门到内核》 专栏

深入Linux操作系统腹地,从命令、脚本到系统编程,探索Linux的运作奥秘。

👉 点击关注专栏


作者:孤廖
学习方向:C++/Linux/算法
人生格言:折而不挠,中不为下

正文:

B3644 【模板】拓扑排序 / 家谱树

在这里插入图片描述

【解法】

【参考代码】
#define _CRT_SECURE_NO_WARNINGS
//B3644 【模板】拓扑排序 / 家谱树
#include <iostream>
#include <vector>
#include <queue>
using namespace std;const int N = 110;
vector<int> edges[N];//edges[i]:表示i节点的孩子 邻接表(孩子表示法)
int d[N];//d[i]:i节点的入度数
int n;
int main()
{cin >> n;//处理图的信息for (int i = 1; i <= n; i++){int j;while (cin >> j, j){edges[i].push_back(j);d[j]++;//更新每个节点的入度数}}//拓扑排序queue<int> q;//先把入度数为0 的点 加入队列for (int i = 1; i <= n; i++) if (d[i] == 0) q.push(i);while (q.size()){int a = q.front(); q.pop();///出队cout << a << " ";//更新入度数for (auto& e : edges[a]){d[e]--;if (d[e] == 0) q.push(e);}}return 0;
}

P2712 摄像头

在这里插入图片描述

【解法】:

拓扑排序判断是否有环。
直接跑⼀遍拓扑排序,然后统计⼀下有多少摄像头没有出队。那么这些没有出队的摄像头就是环⾥⾯的元素。
注意:
• 有些位置可能没有摄像头,需要判断⼀下。

【参考代码】

#define _CRT_SECURE_NO_WARNINGS
//P2712 摄像头#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 510;
vector<int> edges[N];//edges[i]:表示i 的边的信息即i指向的孩子
int n;//摄像头的个数
int d[N];//d[i]:表示节点i的入度数
bool st[N];//st[i]:表示i位置是否有摄像头
int main()
{cin >> n;//输入摄像头的信息 即图的信息for (int i = 1; i <= n; i++){int x, m, y;cin >> x >> m;st[x] = true;while (m--){cin >> y;edges[x].push_back(y);//统计节点入度数d[y]++;}}//拓扑排序queue<int> q;//将入度数 为0的摄像头入队for (int i = 0; i <= 500; i++){if (d[i] == 0 && st[i] ) q.push(i);}while (q.size()){//将入度数为0的摄像头 出队int a = q.front(); q.pop();//更新与a摄像头相关的摄像头的入度数for (auto& e : edges[a]){d[e]--;if (st[e] && d[e] == 0) q.push(e);}}//遍历所有位置找到是否还有没有砸坏的摄像头int ret = 0;//还没砸掉的摄像头数量for (int i = 0; i <= 500; i++){if (st[i] && d[i]) ret++;}if (ret == 0) cout << "YES " << endl;else cout << ret << endl;return 0;
}

P4017 最大食物链计数

在这里插入图片描述
在这里插入图片描述

【解法】

注意审题!题⽬问的是⼀共有多少条路径!
拓扑排序的过程中,进⾏动态规划。
对于每⼀个节点 ,通过它的路径为:前驱所有结点的路径总数之和。因此,可以在拓扑排序的过程中,维护从起点开始到达每⼀个节点的路径总数。

【参考代码】

#define _CRT_SECURE_NO_WARNINGS//P4017 最大食物链计数#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 5010,M=5e5+10,MOD= 80112002;int f[N];//f[i]:表示从生产者到i种生物的最大食物链条数
vector<int> edges[M];//边数
int in[N];//in[i]:表示:i种能被多少种生物吃掉即入度数
int out[N];//out[i]:表示:i种生物能吃多少种生物即出度数
int n, m;//物种的个数 和食物链的条数
int main()
{cin >> n >> m;for (int i = 1; i <= m; i++){int x, y;//x->ycin >> x >> y;//统计边的信息,和每个节点 的出入度情况edges[x]. push_back(y);in[y]++;out[x]++;}//dp+拓扑排序//初始化 初始化生产者的最大食物链条数 即为1queue<int> q;for (int i = 1; i <= n; i++){if (in[i] == 0){f[i] = 1;//初始化q.push(i);//生产者入队}}while (q.size()){int a = q.front(); q.pop();//入度数为0的生物出队//更新a的捕食者的最大生物链条数for (auto& e : edges[a]){//a->ein[e]--;f[e] = (f[e] + f[a]) % MOD;//模加模防止溢出if (in[e] == 0)  q.push(e);}}//统计不同食物网最大的食物链条数总和int ret = 0;//结果for (int i = 1; i <= n; i++){if (out[i] == 0) ret = (ret + f[i]) % MOD;//模加模防止溢出}//输出结果cout << ret << endl;return 0;
}

3 :P1113 杂务

在这里插入图片描述
在这里插入图片描述

【解法】

拓扑排序的过程中,进⾏动态规划。
对于每⼀个事件 ,完成它的最⼩时间为:完成前驱所有事件的最⼩时间中的最⼤值 + 当前事件的完
成时间。因此,可以在拓扑排序的过程中,维护每⼀个事件完成的最⼩时间,然后更新当前事件的最⼩时间。

第一种初始化下:


#define _CRT_SECURE_NO_WARNINGS
//P1113 杂务#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e4 + 10;
int f[N];//f[i]:i杂物完成所需的最小时间
int n;//n 个杂物
vector<int> edges[N];//edges[i]:杂物i完成后能完成的杂物
int len[N];//len[i]:杂物i完成需要的时间
int in[N];//in[i]:杂物i的入度即做杂物i前 需要完成的其他杂物个数
int main()
{cin >> n;for (int i = 1; i <= n; i++){int b = 0, a = 0;//当前杂物b 作杂务b前需要完成的杂物acin >> b >> len[b];while (cin >> a, a){edges[a].push_back(b);in[b]++;}}//dp +拓扑排序//默认初始化int ret = 0;//结果queue<int> q;for (int i = 1; i <= n; i++){if (in[i] == 0) q.push(i);}while (q.size()){int pre = q.front(); q.pop();//出队f[pre] += len[pre];ret = max(ret, f[pre]);//更新后续要完成杂物的最小时间for (auto& e : edges[pre]){in[e]--;f[e] = max(f[e], f[pre]);if (in[e] == 0) q.push(e);}}//输出结果cout << ret << endl;return 0;
}

第二种初始化下

#define _CRT_SECURE_NO_WARNINGS
//P1113 杂务#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e4 + 10;
int f[N];//f[i]:i杂物完成所需的最小时间
int n;//n 个杂物
vector<int> edges[N];//edges[i]:杂物i完成后能完成的杂物
int len[N];//len[i]:杂物i完成需要的时间
int in[N];//in[i]:杂物i的入度即做杂物i前 需要完成的其他杂物个数
int main()
{cin >> n;for (int i = 1; i <= n; i++){int b = 0, a = 0;//当前杂物b 作杂务b前需要完成的杂物acin >> b >> len[b];while (cin >> a, a){edges[a].push_back(b);in[b]++;}}//dp +拓扑排序//初始化int ret = 0;//结果queue<int> q;for (int i = 1; i <= n; i++){if (in[i] == 0){q.push(i);f[i] = len[i];}}while (q.size()){int pre = q.front(); q.pop();//出队//更新后续要完成杂物的最小时间ret = max(ret, f[pre]);for (auto& e : edges[pre]){in[e]--;f[e] = max(f[e], len[e] + f[pre]);if (in[e] == 0) q.push(e);}}//输出结果cout << ret << endl;return 0;
}



结语:

这篇拓扑排序专题,我们从四道洛谷题里挖出了算法 “不变的核心” 与 “多变的场景”——B3644 作为模板题,用 C++ 的vector邻接表 +queue搭好了 Kahn 算法的基础框架,让 “入度维护 + 节点遍历” 的逻辑变得直观P2712 摄像头则在模板上加了 “环检测” 的实际需求,靠 “拓扑序列长度与节点数对比” 快速破题,C++ 的st数组又帮我们精准筛选出有摄像头的位置,避免无效计算;P4017 最大食物链计数更巧妙,把拓扑排序和 DP 结合,用f数组同步累加路径数,模运算的细节处理还兼顾了数据溢出问题;而 P1113 杂务则聚焦 “最长路径”,两种初始化方式的对比,让我们看清f数组维护 “前置任务最大时间” 的本质,也体会到 C++ 代码实现的灵活性。

其实拓扑排序的魅力,就在于它能把 “依赖关系” 转化为 “可计算的顺序”,而 C++ 则是把这种思路落地的关键 —— 选queue还是priority_queue,用数组还是vector存状态,甚至初始化的时机,都会影响代码的效率与可读性。这也是 “算法磨剑” 的意义:不只是会背模板,更是能根据题目场景,用 C++ 把算法 “磨” 成贴合需求的工具。

如果这篇解析帮你理清了拓扑排序的不同应用场景,或是对 C++ 实现细节有了新理解,不妨点个赞 + 收藏,方便后续复盘;也欢迎在评论区分享你的思考:你解 P2712 时有没有先想到其他判环方法?P1113 的两种初始化方式,你更偏爱哪种逻辑?后续 “算法磨剑:用 C++ 思考的艺术” 专栏还会继续深挖图论、动态规划等算法的实战场景,陪你从 “会用算法” 到 “用好算法”,在代码里一步步精进!

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

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

相关文章

盲盒抽卡机小程序:从0到1的蜕变之路

盲盒抽卡机小程序从概念提出到最终上线&#xff0c;经历了从0到1的蜕变过程。这个过程充满了挑战与机遇&#xff0c;也凝聚了开发团队的智慧和汗水。本文将分享盲盒抽卡机小程序的开发历程&#xff0c;探讨其背后的技术实现和市场策略。需求分析&#xff1a;明确目标用户与市场…

分层-三层架构

文章目录介绍代码拆分Dao层server层controller层运行结果介绍 在我们进行程序设计以及程序开发时&#xff0c;尽可能让每一个接口、类、方法的职责更单一些&#xff08;单一职责原则&#xff09;。 单一职责原则&#xff1a;一个类或一个方法&#xff0c;就只做一件事情&#…

Vue2 VS Vue3

vue3 是的&#xff0c;Vue 3 确实取消了基于 JavaScript 原型的 Vue 和 VueComponent 构造函数&#xff08;即你提到的 vm 和 vc&#xff09;&#xff0c;取而代之的是一种完全不同的、基于普通对象和代理&#xff08;Proxy&#xff09;的实例管理方式。 这是一个颠覆性的改变…

Vue3入门到实战,最新版vue3+TypeScript前端开发教程,Vue3简介,笔记02

笔记02 一、Vue3简介 1.1、Vue3发布日期&#xff1a; 2020年9月18日 1.2、Vue3做了哪些升级&#xff1a; 1.2.1、性能的提升 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 打包大小减少41%初次渲染快55%更新渲染快133%内容减少54% 1.2.2、源码的优化…

.net core webapi/mvc阿里云服务器部署 - 错误解决

错误及解决方案缺少web.config配置HTTP 错误 500.19 - Internal Server Error检查 IIS 配置1. 确保 .NET Core Hosting Bundle 已安装2. 检查 应用程序池 配置3. 检查 IIS MIME 类型检查文件权限1. 确保 IIS 用户 有权限访问网站目录2. 检查 web.config 文件权限启用详细错误日…

多输入(input)多输出(output)验证

#作者&#xff1a;程宏斌 文章目录前言Flb 1.9.4 INCLUDE配置测试测试方案测试配置文件测试命令Flb 3.0.2 INCLUDE配置测试测试方案测试配置文件启动命令结论结论一&#xff1a;结论二&#xff1a;前言 需要设计并执行一组测试用例&#xff0c;这些测试用例将包括以子文件形式…

行业学习【电商】:垂直电商如何理解?以专业宠物平台为例

声明&#xff1a;以下部分内容含AI生成 “宠物等爱好者的专业平台”指的是垂直电商的一个具体例子。 “垂直电商” 就是指不卖所有东西&#xff0c;只深耕某一个特定领域&#xff08;即“垂直”领域&#xff09;的电商平台。 “宠物爱好者的专业平台”就是这样一个专门为养宠…

GPT(Generative Pre-trained Transformer)模型架构与损失函数介绍

目录 一、核心架构&#xff1a;Transformer Decoder 1. 核心组件&#xff1a;仅解码器&#xff08;Decoder-Only&#xff09;的堆叠 2. 输入表示&#xff1a;Token 位置 3. 输出 二、训练过程&#xff1a;两阶段范式 阶段一&#xff1a;预训练&#xff08;Pre-training&…

GitHub 热榜项目 - 日榜(2025-09-10)

GitHub 热榜项目 - 日榜(2025-09-10) 生成于&#xff1a;2025-09-10 统计摘要 共发现热门项目&#xff1a;15 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点&#xff1a;LLM智能体应用爆发&#xff08;如parlant、AutoAgent&#xff09;&a…

论文阅读:arxiv 2023 Large Language Models are Not Stable Recommender Systems

总目录 大模型相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 https://arxiv.org/pdf/2312.15746 速览 破解大语言模型在推荐系统中的不稳定性 该论文聚焦于大语言模型&#xff08;LLMs&#xff09;在推荐系统中的应用问题&#xff0c;指出…

Linux的使用——FinalShell下载使用及连接云服务器的教程

一、注册免费阿里云服务器 1. 进入阿里云服务器官网 阿里云-计算&#xff0c;为了无法计算的价值https://www.aliyun.com/?spm5176.ecscore_server.console-base_top-nav.dlogo.39144df5uvPLOm 2. 点击免费试用 这里我已经试用过了&#xff0c;大家选择合适的云服务器点击立…

如何清理 Docker 占用的巨大磁盘空间

我相信很多人在使用 Docker 一段时间后&#xff0c;都会遇到一个常见问题&#xff1a;磁盘空间被迅速吃光&#xff0c;尤其是在进行频繁的镜像构建、测试和运行容器时。以我自己为例&#xff0c;在 Ubuntu 24.04设备上&#xff0c;docker system df -v 一看&#xff0c;Docker …

【CMake】缓存变量

目录 一. 缓存变量 二.创建缓存变量 2.1.使用set()来创建缓存变量 2.2.使用FORCE参数来覆盖缓存变量 2.2.1.示例1——不带force的set是不能覆盖已经存在的缓存变量的 2.2.2.示例2——带force的set才能覆盖已经存在的缓存变量 2.2.3.对比示例 2.3.命令行 -D 创建/覆盖缓…

vue2使用若依框架动态新增tab页并存储之前的tab页的操作

1. 应用场景&#xff1a;点击历史记录&#xff0c;要比较两个tab页的内容时&#xff0c;需要做到切换tab页来回看左右对数据对比。2.开发难点若依项目正常是把路由配置到菜单管理里&#xff0c;都是设定好的。不过它也给我们写好了动态新增tab页的方&#xff0c;需要我们自己来…

论文阅读-SelectiveStereo

文章目录1 概述2 模块2.1 SelectiveIGEV和IGEV的差异2.2 上下文空间注意力2.2.1 通道注意力2.2.2 空间注意力2.3 SRU3 效果参考资料1 概述 本文主要结合代码对Selective的创新点进行针对性讲解&#xff0c;相关的背景知识可以参考我写的另两篇文章论文阅读-RaftStereo和论文阅…

深入分析神马 M56S+ 202T 矿机参数与性能特点

引言在比特币&#xff08;BTC&#xff09;和比特币现金&#xff08;BCH&#xff09;等主流加密货币的挖掘过程中&#xff0c;矿机的选择直接关系到挖矿的效率与收益。神马 M56S 202T矿机是SHA-256算法的矿机&#xff0c;凭借其强大的算力和高效的能效比&#xff0c;成为了矿工们…

36.2Linux单总线驱动DS18B20实验(详细讲解代码)_csdn

想必看过我很多次博客的同学&#xff0c;都知道了编写驱动的流程&#xff01; 这里我们还是按照以前的习惯来一步一步讲解&#xff01; 单总线驱动&#xff0c;在F103和51单片机的裸机开发中是经常见的。 linux驱动代码编写实际上就是&#xff0c;端对端的编程&#xff01; 就是…

【杂类】应对 MySQL 处理短时间高并发的请求:缓存预热

一、什么是缓存预热&#xff1f;1. 核心概念​​缓存预热&#xff08;Cache Warm-up&#xff09;​​ 是指在系统​​正式对外提供服务之前​​&#xff0c;或​​某个高并发场景来临之前​​&#xff0c;​​主动​​将后续极有可能被访问的热点数据从数据库&#xff08;MySQL…

点评项目(Redis中间件)第三部分短信登录,查询缓存

可以直接看后面Redis实现功能的部分基于session实现短信登录发送短信验证码前端请求样式业务层代码Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {Overridepublic Result sendCode(String phone, HttpSession se…

线性方程求解器的矩阵分裂

大概思路是对的&#xff0c;但是查老师可能会出现幻觉&#xff0c;小心食用 &#x1f603;这段代码是在初始化迭代法求解器&#xff0c;构建迭代矩阵和分裂矩阵。以下是详细解释&#xff1a; if init_from_func or init_from_input:# 1. 存储刚度矩阵self.stiff_p stiff_p# 2.…