【图论】Tarjan 缩点:解决有向图中环的问题
题目链接P3387 【模板】缩点 / 强连通分量 - 洛谷有向无环图DAG具有很多很好用的性质因为没有环而且有向无环图必定有一个点是入度为 0 也必定有个点是出度为 0 所以非常适合拓扑排序也因为没有环所以比如找最优路径或是路径方案数的时候可用动态规划 DP 很快速就能解决。但有些情况下可能给到的并不是一个有向无环图而只是一个有向图。里面有可能存在环也就无法利用刚刚所提到的性质了。可如果可以把环去掉把有向图转化成一个有向无环图那我们就能利用有向无环图的性质去解题了。而 Tarjan 缩点就是把有向图转变成有向无环图的方法。如洛谷 P3387 题目我们很快其实就能发现要找这个权值最大的路径第一想法当然是进行动态规划 DP 。但是题目提供的图并不是一个有向无环图而是一个有向图是可能有环的。而一旦有环在进行 DP 的过程就很麻烦还可能死循环。在了解 Tarjan 算法之前先看看环是如何导致 DP 难以进行的。由于环的存在很可能在图上走的时候就会突然回到之前走过的节点。当然可以标记每个点是否去过但是这样一来还有问题是无法找到一个结束循环的地方。因为不能一概而论的碰到走过的节点就不继续走下去也不能一直走下去。但是可以发现一个关键点是由于题目说“可以重复走但是走过的节点只算一次”那也就是说碰到环的话那肯定得走完一圈回来才值。对于图上的一些可以互相到达的节点的集合称为强连通分量SCC。一个环或者多个环嵌套一起都算一个强连通分量因为他们任意节点都可以相互到达而一个孤立的节点是一个特殊的强连通分量。更进一步可以发现其实在图上对于任意一个强连通分量只要碰到它们内部任意一个节点那我肯定得走完全部节点得到全部权值才是赚的。所以说最优的策略其实就是到达一个强连通分量就把内部所有节点的权值都加上那这个强连通分量就有点类似可以浓缩成一个“节点”。如下图容易发现图中 2 3 4 5 6 组成的环就是一个强连通分量而 Tarjan 缩点的策略就是把这一整个强连通分量如图示缩成一个点这个点会拥有原本强连通分量的所有性质比如仍然可以到达 7 8 这两个点且一整个点的权值应该是 2 3 4 5 6 的权值和。缩点方法首先给大家看一个简单的模板再做其他解释。#include bits/stdc.h using namespace std; #define int long long const int maxn2e510; vectorvectorint g(maxn);//原有向图 int dfn[maxn];//指节点i的编号 int low[maxn];//指节点i最多能回到之前的哪个节点也可以说是最早出现的时间戳 bool st[maxn];//用于标注当前节点是否已入栈 stackint sk;//已入栈的节点也代表着之前已经到达过了 setpairint,int s;//用于去重建立新图DAG int cnt;//用于分配编号 int scc[maxn];//scc[i]是i节点在新的DAG图中的对应缩点编号 vectorvectorint dag(maxn);//新图DAG void dfs(int p){ dfn[p]low[p]cnt;//先分配一个编号 st[p]true; sk.push(p);//标注true并入栈 //遍历当前节点的所有出边 for(int i:g[p]){ //如果dfn发现是0说明没来过这个点先进行dfs再更新low值取最小值是为了找能到达最早的节点 //如果这个点之前走过了而发现它还在栈内节点p可以回到这个i点可能形成环直接更新low值 if(!dfn[i]){ dfs(i); low[p]min(low[p],low[i]); }else if(st[i]){ low[p]min(low[p],dfn[i]); } } //如果dfn和low值相等说明这个p节点是缩点的根节点 if(dfn[p]low[p]){ //此时栈内的节点一直到p节点都是同一缩点内的一直从栈内取出节点即可 while(true){ int tsk.top(); sk.pop(); st[t]false;//取出后pop并标记变为false scc[t]p;//标记上缩点的编号 if(tp) break;//到p节点说明当前强连通分量已经遍历完了break } } } void solve(){ int n,m; cinnm; for(int i1;im;i){ int u,v; cinuv; g[u].push_back(v); } //遍历所有点进行缩点 for(int i1;in;i){ if(!dfn[i]) dfs(i); } //找出有效的新图DAG的边去重 for(int i1;in;i){ for(int j:g[i]){ if(scc[i]scc[j]) continue;//scc相等说明同在一个缩点内无效边 s.insert({scc[i],scc[j]}); } } //建立新图 for(const auto i:s){ dag[i.first].push_back(i.second); } } signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int t1; //cint; while(t--) solve(); return 0; }注释交代其实已经比较清晰了通过 dfs 的搜索dfn 和 low 来判断当前点是否属于一个 scc 根节点来进行缩点而从 sk 栈中找到属于同一个缩点的节点。整体而言并不算特别复杂。我个人而言经常就在把节点从栈内取出后忘记标记 st 为 false 了其他基本都不会出很大问题。但这里我用的 set 去重这个开销还是稍微大了一点也可以转用 unique 去重。vectorpairint,int s; for(int i1;in;i){ for(int j:g[i]){ if(scc[i]scc[j]) continue;//scc相等说明同在一个缩点内无效边 s.push_back({scc[i],scc[j]}); } } sort(s.begin(),s.end()); s.erase(unique(s.begin(),s.end()),s.end());解题方法知道了缩点的方法后解决这个 P3387 就不算很困难了因为缩点后就是有向无环图 DAG 进行 DP 非常简单。而我们只需要对原本的缩点模板加入一点修改即可修改处我会标出。#include bits/stdc.h using namespace std; #define int long long const int maxn2e510; vectorvectorint g(maxn);//原有向图 int dfn[maxn];//指节点i的编号 int low[maxn];//指节点i最多能回到之前的哪个节点也可以说是最早出现的时间戳 bool st[maxn];//用于标注当前节点是否已入栈 stackint sk;//已入栈的节点也代表着之前已经到达过了 setpairint,int s;//用于去重建立新图DAG int cnt;//用于分配编号 int scc[maxn];//scc[i]是i节点在新的DAG图中的对应缩点编号 vectorvectorint dag(maxn);//新图DAG int arr[maxn];//原图权值 int sum[maxn];//累加缩点后的权值 void dfs(int p){ dfn[p]low[p]cnt;//先分配一个编号 st[p]true; sk.push(p);//标注true并入栈 //遍历当前节点的所有出边 for(int i:g[p]){ //如果dfn发现是0说明没来过这个点先进行dfs再更新low值取最小值是为了找能到达最早的节点 //如果这个点之前走过了而发现它还在栈内节点p可以回到这个i点可能形成环直接更新low值 if(!dfn[i]){ dfs(i); low[p]min(low[p],low[i]); }else if(st[i]){ low[p]min(low[p],dfn[i]); } } //如果dfn和low值相等说明这个p节点是缩点的根节点 if(dfn[p]low[p]){ //此时栈内的节点一直到p节点都是同一缩点内的一直从栈内取出节点即可 while(true){ int tsk.top(); sk.pop(); st[t]false;//取出后pop并标记变为false scc[t]p;//标记上缩点的编号 //// sum[p]arr[t];//新增为缩点累加权值 //// if(tp) break;//到p节点说明当前强连通分量已经遍历完了break } } } //dp逻辑 int dp[maxn]; int dpdfs(int p){ if(dp[p]!-1) return dp[p]; int ans0; for(int i:dag[p]){ ansmax(ans,dpdfs(i)); } dp[p]anssum[p]; return dp[p]; } void solve(){ int n,m; cinnm; for(int i1;in;i) cinarr[i]; for(int i1;im;i){ int u,v; cinuv; g[u].push_back(v); } //遍历所有点进行缩点 for(int i1;in;i){ if(!dfn[i]) dfs(i); } //找出有效的新图DAG的边去重 for(int i1;in;i){ for(int j:g[i]){ if(scc[i]scc[j]) continue;//scc相等说明同在一个缩点内无效边 s.insert({scc[i],scc[j]}); } } //建立新图 for(const auto i:s){ dag[i.first].push_back(i.second); } memset(dp,-1,sizeof(dp));//初始化 int ans0; for(int i1;in;i){ ansmax(ans,dpdfs(scc[i])); } coutans\n; } signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int t1; //cint; while(t--) solve(); return 0; }

相关新闻

请求绑定 binding(binding/ 包)

请求绑定 binding(binding/ 包)

1.1 Binding 接口源码位置:binding/binding.go:30-35// Binding describes the interface which needs to be implemented for binding the // data present in the request such as JSON request body, query parameters or // the form POST. type Binding interface {Name()…

2026/8/26 16:10:17 阅读更多 →
多Agent系统中的子Agent通信机制:三大经典范式深度解析

多Agent系统中的子Agent通信机制:三大经典范式深度解析

多Agent系统中的子Agent通信机制:三大经典范式深度解析 一、引言 随着大语言模型(LLM)能力的飞速发展,单Agent系统在处理复杂任务时逐渐暴露出能力边界有限、鲁棒性不足等问题。多Agent系统应运而生,通过多个专业化Age…

2026/8/26 15:58:28 阅读更多 →
Hi9263实地150V 如何让32串BMS高压取电稳定可靠功耗低

Hi9263实地150V 如何让32串BMS高压取电稳定可靠功耗低

引言从高压动力电池母线直接取电、一步降压给 MCU / AFE / 通信模块供电——这颗 ESOP8 小封装芯片,正在成为 BMS 与众多高压系统的供电主力。01 为什么 BMS 需要一颗高压低功耗的 DCDC新能源浪潮下,电池管理系统(BMS)是电池组的「…

2026/8/26 15:54:50 阅读更多 →

最新新闻

【Playwright教程】Playwright必备基础知识、核心用途与第一个截图实战

【Playwright教程】Playwright必备基础知识、核心用途与第一个截图实战

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:软件测试学习提升资料包 🔥 教程推荐:自动化测试从入门到精通全套保姆级教程 📢欢迎点赞 👍 收藏 ⭐留言 学习 Playwri…

2026/8/26 18:51:29 阅读更多 →
一文学完linux必要点

一文学完linux必要点

本文仅作为了解使用linux。(个人笔记) 目录 一、linux认知与命令格式 1、 命令格式 1)选项的两种风格: 2)帮助系统 二、文件与目录操作 1、浏览与定位 1)ls 2)cd 2、创建操作 3、删除…

2026/8/26 18:51:29 阅读更多 →
langchain1.X学习笔记-30-中间件Middleware之自定义中间件(三)装饰器和类的选择

langchain1.X学习笔记-30-中间件Middleware之自定义中间件(三)装饰器和类的选择

文章目录 1 模型初始化 2 装饰器和类的选择 2.1 情况1(一钩用装,多钩用类) 2.1.1 使用装饰器实现 2.1.2 使用类实现 2.2 情况2(复杂配置推荐用类实现) 2.3 情况3(跨项目复用推荐用类写法) 2.4 总结 3 hook函数执行顺序(重要) 当一个中间件只需要实现一个钩子函数时,直接使用装…

2026/8/26 18:51:29 阅读更多 →
高性能推拉力测试仪采购,这5个参数不知道就亏大了!

高性能推拉力测试仪采购,这5个参数不知道就亏大了!

在微电子封装、半导体键合与精密制造领域,推拉力测试仪早已不是简单的“测力工具”,而是产线质量管控与失效分析的核心设备。然而,面对市场上从数万到数十万不等的报价,不少采购负责人发现:高价买回的设备在测试微小焊…

2026/8/26 18:51:29 阅读更多 →
IriSig-Spoof:面向时间鲁棒卫星射频指纹识别与欺骗检测的真实世界基准

IriSig-Spoof:面向时间鲁棒卫星射频指纹识别与欺骗检测的真实世界基准

大家读完觉得有帮助记得关注和点赞!!! 摘要 低地球轨道(LEO)卫星互联网正成为关键通信基础设施,然而其开放的无线链路仍然容易受到卫星冒充和信号欺骗的攻击。射频指纹识别(RFF)通…

2026/8/26 18:51:29 阅读更多 →
信奥梯队选拔面试中遇到难题孩子直接放弃怎么办

信奥梯队选拔面试中遇到难题孩子直接放弃怎么办

面试中遇到孩子碰到难题直接放弃的情况,核心要分「面试现场即时引导」和「后续分层处置」两步处理,既不浪费高潜力苗子,也能精准筛选出适配梯队的学员。 一、面试现场即时引导操作 1、‌先降低情绪压力‌ 第一时间停止计时,温和…

2026/8/26 18:50:27 阅读更多 →

日新闻

Python random 模块常用函数详解:从入门到实战

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 0:00:40 阅读更多 →
《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》索引目录: 《Microsoft Sql server 2008 Internals》读书笔记--目录索引 在上篇文章中,主要介绍了创建数据库的基本语法和FileGroup的初步知识。需要注意的是: 关于FileGroup 如果你的系统是用Raid设备直接存…

2026/8/26 1:18:18 阅读更多 →
政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体已经从概念试点阶段,转入了政务服务的常态化落地应用;在实际使用过程中,它能自主理解办事需求、辅助完成填报申报、开展材料预审,并联动多个系统协同作业,真正嵌入到政务办理的全流程当中。但在落地推进过…

2026/8/26 1:18:18 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/26 14:45:33 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/26 17:46:43 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 14:46:37 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/26 17:46:39 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →