Toposort

news/2025/10/20 16:53:37/文章来源:https://www.cnblogs.com/czh-ak-NOI/p/19153051

拓扑排序

今天2025.10.16

经过 \(hwh\)\(cxy\) 的指导,我也是初步学会了 \(markdown\),必须得写一篇文章练练手。写得格式可能不太好,没逝,一定会好起来的

好了,开始说说今天学的东西—— 拓扑排序

事实上我早就看过了,只是一直没实践,今天一上手,发现拓扑排序的绿题确实简单

\(DAG\) 定义

众所周知,我们在生活中,经常会遇到要做的一些事情要有一定顺序,例如:

要做饭,得买菜,然后洗菜、切菜,接着进行一系列的科技与狠活 煮饭(或者蒸,炸,煎等操作),最后,饭就做好了。

用这个例子就是说,我在做饭之前需要进行一系列的准备,都做完以后,才可以做饭,这就有了各个事情之间的先后顺序

我们就可以从中抽象出数学模型了——\(DAG\)(有向无环图)

仍以做饭为例:

日常吃饭流程

这就是一个\(DAG\),当然,我们OI肯定不可能把汉字作为节点,所以,我们就再次把每个事件进行编号,就变成了比较好看的图:

和某节点相连的边的数量就是该节点的度

又分为入度出度

某节点的入度就是该节点被几条边入 开玩笑,就是以该节点为结尾的边的数量,例如上图的7个节点从1至7的入度依次为:
\(0,1,1,1,1,1,3\)

节点的出度自然就是以该节点为起点的边的数量,例如上图的7个节点从1至7的出度依次为:
\(1,1,3,1,1,1,0\)

拓扑序

拓扑序列是有向图中将所有顶点排列成满足前驱关系的线性序列,由某个集合上的偏序关系转化为全序关系的操作结果

不必看这个定义,我说一下自己的理解,其实就是所做的每件事情的先后顺序就是一个拓扑序,仍然以做饭为例:

买菜 \(\rightarrow\) 洗菜 \(\rightarrow\) 切菜 \(\rightarrow\)\(\rightarrow\)\(\rightarrow\)\(\rightarrow\) 上菜

这就是一个拓扑序。不难发现,煮、炸、蒸,之间是并列关系,所以,还有别的拓扑序:

买菜 \(\rightarrow\) 洗菜 \(\rightarrow\) 切菜 \(\rightarrow\)\(\rightarrow\)\(\rightarrow\)\(\rightarrow\) 上菜

买菜 \(\rightarrow\) 洗菜 \(\rightarrow\) 切菜 \(\rightarrow\)\(\rightarrow\)\(\rightarrow\)\(\rightarrow\) 上菜

.....

一共 \(3!=6\) 种拓扑序,不一一列出了

变成数字编号:

\(1,2,3,4,5,6,7\)

\(1,2,3,5,4,6,7\)

\(1,2,3,6,4,5,7\)

...

还是 \(6\) 种,区别就是 \(4,5,6\) 的排列不同

当然了,拓扑序用字母表示节点也可以:

好了,基础知识就介绍到这里了。

代码思路

\(n\) 个节点 \(m\) 条边的 \(DAG\) 的一个拓扑:

首先维护一个队列,我们每次都要将没入过队的且入度为 \(0\) 的节点加入这个队列,然后每次取出队头,接着把这个节点的子节点都入队,不停进行下去,直到队列为空。

输出一种拓扑序 只需在节点出队的时候输出该节点就可以(记住,必须是 \(DAG\) 才可以这样输出,如果题目 不保证无环或连通 就不能这样,需要另开一个 \(ans\) 队列记录弹出的节点,最后判断一下 ans.size() 是否等于 \(n\) ,如果为 ,就把队列里的数依次弹出并且输出即可,否则就是无解的(不联通或者有环) )(一般题目都会有 Special Judge,没有的话就 自求多福吧, 看情况修改输出)

复杂度易证:\(O(n+m)\) (因为输入 \(m\) 条边,且遍历图的时候,\(n\) 个节点都被遍历到了)

补充:不难发现,一般来说,题目所给的图都是森林,所以为了 偷懒 方便,直接建立超级源点,让初始所有入度为 \(0\) 的点都成为超级源点的儿子。超级源点很多时候能把题目所给的节点信息都变成边权信息,所以必须得会,本文不多介绍,其实很简单,知道定义以后自己都会编码。

例题

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

简介:

给了 \(N\)\(1≤N≤100\) )个人,接下来 \(N\) 行,第 \(i\) 行给出第 \(i\) 个人的后代,输入 \(0\) 时结束这一行。

输出:

输出一个序列,使得每个人的后辈都比那个人后列出。如果有多种不同的序列,输出任意一种即可。

样例:

输入:
5
0
4 5 1 0
1 0
5 3 0
3 0

输出:
2 4 5 3 1

不废话了,板题直接上代码:

//为了偷懒所以建了超级源点,但是,这确实是个好东西,后面的题会用到,不如提前学会这种写法
#include<bits/stdc++.h>
using namespace std;
const int N=105,M=N*N;
int n,cnt,ans[N],d[N],hd[N],nxt[M],to[M];//我的链式前向星一般不写结构体(但是从内存访问的常数角度上来说,结构体常数更小,但是分开的数组码代码的时候比较快)我就直接分开写了
void add(int a,int b){//链式前向星加边,本题 N 较小,直接邻接矩阵也可以nxt[++cnt]=hd[a];hd[a]=cnt;to[cnt]=b;
}
void Toposort(int s){//拓扑排序主体queue<int>q;//定义队列q.push(s);//加入超级源点while(!q.empty()){int u=q.front();//取队首q.pop();if(u!=n+1) cout<<u<<" ";//不是超级源点的就输出(根据题意,一定有解,直接输出就好了)for(int i=hd[u];i;i=nxt[i]){int t=to[i];//遍历儿子节点if(--d[t]==0){//给儿子节点的入度-1q.push(t);//如果儿子节点入度为0,就入队}}}
}
int main(){cin>>n;int S=n+1;//建立超级源点for(int i=1;i<=n;i++){int k;while(1){cin>>k;if(k==0) break;d[k]++;//儿子节点入度+1add(i,k);//加边}}for(int i=1;i<=n;i++) if(d[i]==0) d[i]++,add(S,i);//入度为零的点,直接变成超级源点的儿子Toposort(S);//求拓扑序,从超级源点开始return 0;
}

P4017 最大食物链计数

题目自己看就好了,一道很简单的 \(DAG上的DP\)

思路:

不难发现,只需把父节点的路径数加到其子节点上即可。初始 入度为0 的节点,路径数 初始化为1,输出 所有出度为0的节点的路径数之和 就是答案

当然,依旧用 超级源点超级汇点 ,这样只需 初始化超级源点的路径数为1,最后输出 超级汇点的路径数 即为答案

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5005,M=5e5+5,mod=80112002;
int n,m,cnt,d1[N],d2[N],hd[N],nxt[M],to[M],cnts[N];//d1为入度,d2为出度,cnts数组是路径数
void add(int a,int b){nxt[++cnt]=hd[a];hd[a]=cnt;to[cnt]=b;
}
void Toposort(int s){queue<int>q;q.push(s);cnts[s]=1;while(!q.empty()){int u=q.front();q.pop();for(int i=hd[u];i;i=nxt[i]){int t=to[i];cnts[t]=(cnts[t]+cnts[u])%mod;if(--d1[t]==0) q.push(t);}}
}
int main(){cin>>n>>m;int S=n+1,T=n+2;//超级源点S,超级汇点Tfor(int i=1;i<=m;i++){int a,b;cin>>a>>b;add(b,a);d1[a]++;d2[b]++;}for(int i=1;i<=n;i++) if(d1[i]==0) d1[i]++,add(S,i);//初始入度为0,加入超级汇点的儿子for(int i=1;i<=n;i++) if(d2[i]==0) d2[i]++,add(i,T);//出度为0,加入超级汇点的父亲Toposort(S);cout<<cnts[T];return 0;
}

P1347 排序

这道题就是需要判断是否不连通或者有环的情况

#include<bits/stdc++.h>
using namespace std;
#define mp(c) c-'A'+1
const int M=605;
int n,m,cnt,hd[28],nxt[M],to[M],c1,d[28],d1[28],a[M],b[M];
bool vis[28];
void add(int a,int b){nxt[++cnt]=hd[a];hd[a]=cnt;to[cnt]=b;d[b]++;
}
bool Toposort(int k){queue<int>q,ans;bool flag=false;for(int i=1;i<=n;i++) d1[i]=d[i];for(int i=1;i<=n;i++) if(vis[i]&&d1[i]==0) q.push(i);if(q.size()>1) flag=true;while(!q.empty()){int u=q.front();q.pop();ans.push(u);for(int i=hd[u];i;i=nxt[i]){int t=to[i];if(--d1[t]==0) q.push(t);if(q.size()>1) flag=true;}}if(ans.size()<c1){cout<<"Inconsistency found after "<<k<<" relations.";return true;}if(ans.size()<n) return false;if(flag) return false;cout<<"Sorted sequence determined after "<<k<<" relations: ";while(!ans.empty()){cout<<(char)(ans.front()+'A'-1);ans.pop();}cout<<".";return true;
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){char s[10];cin>>s;a[i]=mp(s[0]),b[i]=mp(s[2]);}add(a[1],b[1]);if(a[1]==b[1]){cout<<"Inconsistency found after 1 relations.";return 0;}vis[a[1]]=vis[b[1]]=true;c1=2;for(int i=2;i<=m;i++){if(a[i]==b[i]){cout<<"Inconsistency found after "<<i<<" relations.";return 0;}if(!vis[a[i]]) vis[a[i]]=true,c1++;if(!vis[b[i]]) vis[b[i]]=true,c1++;add(a[i],b[i]);if(Toposort(i)) return 0;}cout<<"Sorted sequence cannot be determined.";return 0;
}

luogu P6145 [USACO20FEB] Timeline G

这就是使用 超级源点节点信息变成边权 的一道题(自己认为是道好题)

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N=1e5+5;
int n,m,C,cnt,hd[N],nxt[N<<1],to[N<<1],w[N<<1],dis[N],d[N];
void add(int a,int b,int c){nxt[++cnt]=hd[a];hd[a]=cnt;to[cnt]=b;w[cnt]=c;d[b]++;
}
void Toposort(int s){queue<int>q;q.push(s);dis[s]=0;while(!q.empty()){int u=q.front();q.pop();for(int i=hd[u];i;i=nxt[i]){int t=to[i];dis[t]=max(dis[t],dis[u]+w[i]);if(--d[t]==0) q.push(t);}}
}
int main(){cin>>n>>m>>C;int S=n+1;for(int i=1;i<=n;i++){int k;cin>>k;add(S,i,k);//用超级源点把节点信息变成边权超级源点和该节点的边权}for(int i=1;i<=C;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c);}Toposort(S);for(int i=1;i<=n;i++) cout<<dis[i]<<endl;return 0;
}

继续更新:

今日2025.10.20

又被侯老师叫去做图论题了,不过正好,遇到了一类以前不会的题:

luogu P1983 [NOIP 2013 普及组] 车站分级

题目自己看就好了,这里说思路:

我们只需把高等级的车站向低等级的车站建一条有向边,然后跑拓扑排序就好,就是一个 分层 ,车站等级开个数组 \(dis[n]\) 记录即可,以后输出最大的 \(dis\) 就是答案。

但是,没说完,这题 建边有优化,不优化可能会 \(TLE\)\(7\) 个点(侯老师因为这个一直在调,疑似失心疯)。你会发现,对于某一条路线,如果始发站为 \(s\),终点站 \(t\),这之间输入了 \(c\) 个车站,如果 暴力建边,让每个高级站都和低级战建一条边,会建 \(c*(t-s+1-c)\) 条边,为 \(O(n^2)\) 级别,有点大。所以我们用 虚拟点优化 (就是在高级站和低级站之间建立一个 中转站 ),这样,所有高级站向中转站分别建一条有向边,再由中转站向每个低级站分别建一条有向边,即可达到原来的效果。此时,只需建 $c+(s-t+1-c)=s-t+1 $ 条边,优化到了 \(O(n)\) ,但记住,虚拟点不要算在等级内

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int n,m,cnt,hd[N],nxt[N*N],to[N*N],d[N],a[N],dis[N];//dis:车站等级
bool vis[N];
void add(int a,int b){nxt[++cnt]=hd[a];hd[a]=cnt;to[cnt]=b;d[b]++;//入度+1
}
void Toposort(int s){queue<int>q;q.push(s);dis[s]=0;//超级源点等级为0while(!q.empty()){int u=q.front();q.pop();for(int i=hd[u];i;i=nxt[i]){int t=to[i];if(t>n) dis[t]=max(dis[t],dis[u]);//特判,节点编号>n,说明是虚拟中转站,等级不加1else dis[t]=max(dis[t],dis[u]+1);if(--d[t]==0) q.push(t);}}
}
int main(){cin>>n>>m;int S=n+m+1;//超级源点for(int i=1;i<=m;i++){int k,s,t;cin>>k>>s;add(s,n+i);//n+i 第i个是虚拟点,高级站向中转站建边memset(vis,0,sizeof(vis));vis[s]=true;for(int j=2;j<=k;j++){cin>>t;vis[t]=true;add(t,n+i);}for(int j=s+1;j<t;j++) if(!vis[j]) add(n+i,j);//中转站向低级战建边}for(int i=1;i<=n;i++) if(d[i]==0) add(S,i);//初始入度为0的,成为超级源点的儿子Toposort(S);int ans=0;for(int i=1;i<=n;i++) ans=max(ans,dis[i]);cout<<ans;return 0;
}

到现在为止,侯老师还在调,目前 \(\#7\)\(1.03s\),额不对,最新消息,调成全 \(WA\) 了(笑死我了)

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

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

相关文章

2025年冲压件厂家权威推荐榜:新能源/光伏/精密/异形/五金/铝/汽配/不锈钢/家具冲压件源头企业深度解析

2025年冲压件厂家权威推荐榜:新能源/光伏/精密/异形/五金/铝/汽配/不锈钢/家具冲压件源头企业深度解析 一、行业背景与发展趋势 随着制造业的转型升级,冲压件行业正迎来新一轮技术变革。在新能源、光伏、汽车、家电等…

AI 产品测试企业内训 | 两天构建企业级智能体测试能力

关注 霍格沃兹测试学院公众号,回复「资料」, 领取人工智能测试开发技术合集 AI 产品和智能体的快速迭代,给企业测试团队带来了新的挑战: 智能体问答错误率高、插件调用效果不可控 语音识别、文本理解、视觉分析准确…

详细介绍:《掰开揉碎讲编程-短篇》 2025 汉化idea控制台出现乱码解决方案 看完这篇解决不了乱码也是神人了

详细介绍:《掰开揉碎讲编程-短篇》 2025 汉化idea控制台出现乱码解决方案 看完这篇解决不了乱码也是神人了pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display…

探索无限可能:生成式推荐的演进、前沿与挑战【AI业务应用方向】

TL;DR 过去一年间,生成式推荐取得了长足的实质性进展,特别是在凭借大型语言模型强大的序列建模与推理能力提升整体推荐性能方面。基于LLM(Large Language Models, LLMs)的生成式推荐(Generative Recommendations,…

【隐语SecretFlow架构解读】隐私保护模型在线推理系统 SecretFlow-Serving 架构解读

打开链接点亮社区Star,照亮技术的前进之路。每一个点赞,都是社区技术大佬前进的动力Github 地址: https://github.com/secretflow 在传统机器学习场景中,完成模型训练得到模型之后,如何将模型的推理能力应用到实际…

2025年储罐厂家权威推荐榜:钢衬塑储罐,钢塑复合储罐,化工储罐,防腐储罐,PE储罐,盐酸储罐,硫酸储罐,聚丙烯储罐,不锈钢储罐,次氯酸钠储罐

2025年储罐厂家权威推荐榜:钢衬塑储罐,钢塑复合储罐,化工储罐,防腐储罐,PE储罐,盐酸储罐,硫酸储罐,聚丙烯储罐,不锈钢储罐,次氯酸钠储罐 在现代化工、制药、环保等工业领域,储罐作为关键的基础设施,其性能…

多晶硅

等待49700一线 企稳多单

2025 最新推荐!溴化锂回收公司精选榜单:制冷机 / 溶液 / 机组回收服务商权威测评及选择指南

引言 伴随 “双碳” 目标推进与溴化锂制冷设备迭代加速,2025 年国内废旧溴化锂机组、溶液回收需求同比增长 20%,但市场仍存在服务标准混乱、环保合规性参差不齐等问题。部分机构缺乏专业拆解技术,导致铜铝资源回收率…

Qt 解决 ld: framework ‘AGL‘ not found

现象 硬件环境非最新 macOS Tahoe 26 系统 XCode 已经更新到 26 版本$ xcodebuild -version Xcode 26.0.1 Build version 17A400Qt 5.15.2执行 qmake 时出现以下链接错误 ld: framework AGL not found clang++: error:…

技术指标分享--单趋势通道

在技术分析中,均线系统一直是最重要的趋势工具之一。这里分享一个趋势通道的指标就是以 55日指数移动平均线(EMA) 为核心,通过构造“多头线、空头线”两条均线通道,直观地反映市场的长线趋势与多空格局。一、指标…

2025年地坪厂家权威推荐榜:环氧地坪漆,聚氨酯地坪,固化耐磨地坪,防腐地坪,室外路面防滑地坪,运动地面,PVC塑胶地板,魔石地坪公司精选

2025年地坪厂家权威推荐榜:环氧地坪漆,聚氨酯地坪,固化耐磨地坪,防腐地坪,室外路面防滑地坪,运动地面,PVC塑胶地板,魔石地坪公司精选 行业背景与发展趋势 随着现代工业化和城市化进程的加速推进,地坪行业作为…

微算法科技(MLGO)研发突破性低复杂度CFG算法,成功缓解边缘分裂学习中的掉队者问题

在分布式机器学习逐步走向应用化、产品化的当下,边缘计算与隐私保护成为支撑智能化发展的两大支柱。在这一背景下,一家领先的智能算法研究企业微算法科技宣布成功开发出一项重要创新技术:一种用于无线网络中分裂学习…

大语言模型的微调策略

大语言模型的微调策略尽管大语言模型展示出强大的能力,但在特定任务领域,它们往往无法达到最佳效果。通过微调,可以将特定领域的数据集输入模型中,使模型学习该领域的知识,从而优化自身在特定领域的 NLP 任务中的…

结对项目—小学四则运算题目生成器

项目成员 汪雨嫣3223003305、沙吉旦乃吉米丁3223004775GitHub地址 https://github.com/wyy517/WYY517/tree/main/math这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience这个作…

阅读笔记一:程序员的自我定位与成长基石

《程序员修炼之道:从小工到专家》开篇便打破了“程序员只是代码编写者”的刻板认知,将职业素养置于核心位置,为技术从业者指明了从“小工”到“专家”的底层逻辑。在数字化浪潮席卷全球的今天,软件已深度融入社会运…

SQL 子查询与多表 JOIN 用法大全(速查版) - 实践

SQL 子查询与多表 JOIN 用法大全(速查版) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

Excel学习指南

感觉太简单,就边练习边笔记了,根据微信读书的《Excel函数与公式速查手册》来练习,不区分大小写 名称的定义与使用 “名称”就是给一个单元格、单元格区域、公式或常量值起一个易于理解和记忆的别名。 如果引用的数据…

2025 年宁波北仑仓库服务商推荐新世洋集团,港口物流仓储的专业之选宁波北仑仓库推荐

在经济全球化的浪潮下,港口物流仓储行业已成为连接国内外贸易的关键纽带,其重要性不言而喻。随着我国进出口贸易规模的持续扩大,以及电商行业的迅猛发展,对高效、可靠的仓储与物流服务的需求呈井喷式增长。宁波北仑…

2025年聚氨酯厂家权威推荐榜:浇注型聚氨酯/聚氨酯预聚体/聚氨酯胶黏剂/聚氨酯组合料/密封胶/胶辊/制品原料,源头厂家技术实力与产品应用深度解析

2025年聚氨酯厂家权威推荐榜:浇注型聚氨酯/聚氨酯预聚体/聚氨酯胶黏剂/聚氨酯组合料/密封胶/胶辊/制品原料,源头厂家技术实力与产品应用深度解析 行业背景与发展趋势 聚氨酯材料作为高分子材料领域的重要组成部分,近…

02-02串口-单片机发送数据,电脑串口调试助手接收数据

一、硬件准备1块STM32F103C8T6最小系统板 1个ST-LINK烧录器 1个USB-to-TTL 若干杜邦线 二、程序点击查看代码 #include "stm32f10x.h" // Device headervoid My_USART_SendBytes(USART_Typ…