当前位置: 首页 > news >正文

算法竞赛备赛——【图论】拓扑排序

拓扑排序算法

前置知识:

1.DAG图:一个无环的有向图,即有向无环图。

2.AOV网络:在⼀个表示⼯程的有向图中,⽤顶点表示活动,⽤弧表示活动之间的优先关系的有向图称为顶点表示活动的⽹(Activity On Vertex Network),简称AOV⽹。

拓扑排序:其实就是对⼀个DAG图构造拓扑序列的过程。

拓扑排序算法:kahn(卡恩)算法(基于BFS)和 基于DFS的算法。

kahn(卡恩)算法

可以判环

时间复杂度:O(V+E)

有环就没有拓扑序列

#include<bits/stdc++.h>
using namespace std;
using ll=long long;//有向图------>判环 
int n,m;
struct Edge{//只求拓扑序列不用带边权 int to,next;
}e[10005];
int h[105];
int ind[105];//入度 
int cnt;
int topo[105],k=0;void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++; 
} bool kahn(){queue<int> q;for(int i=1;i<=n;++i){if(ind[i]==0){q.push(i);}}int x,y;while(!q.empty()){x=q.front();q.pop();topo[++k]=x;//从1开始计数 for(int i=h[x];i!=-1;i=e[i].next) {y=e[i].to;ind[y]--;if(ind[y]==0) q.push(y); }}if(k==n) return true;//无环 else return false;//成环 
} int main(){cin>>n>>m;memset(h,-1,sizeof h);int u,v;for(int i=1;i<=m;++i){cin>>u>>v;add(u,v);ind[v]++;}if(kahn()){for(int i=1;i<=k;++i){cout<<topo[i]<<" ";}} else{cout<<"有环\n"; }return 0;
} 

例题

P1113 杂务 - 洛谷

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int n; 
struct Edge{int to,next;
}e[100005];
int h[10005],cnt;
int ind[10005];
int t[10005];//每个点需要的时间 
int ed[10005];//每个点的完成时间 void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++;
}void kahn(){queue<int> q;for(int i=1;i<=n;++i){if(ind[i]==0){q.push(i);ed[i]=t[i];//没有前置活动 }}int u,v;while(!q.empty()){u=q.front();q.pop();for(int i=h[u];~i;i=e[i].next){v=e[i].to;ind[v]--;ed[v]=max(ed[v],ed[u]+t[v]); //更新y的结束时间 if(ind[v]==0){q.push(v);}} }
}int main(){cin>>n;int u,v;memset(h,-1,sizeof h);for(int i=1;i<=n;++i){cin>>u;cin>>t[u];//注意不要同行读入while(cin>>v){if(v==0) break;add(u,v);ind[v]++;}}kahn();int ans=-1;for(int i=1;i<=n;++i){ans=max(ans,ed[i]);} cout<<ans<<endl;return 0;
} 

[P1983 NOIP 2013 普及组] 车站分级 - 洛谷

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int n,m; 
vector<int> g[1005];
int s[1005];
int flag[1005];
int pd[1005][1005];//判断i--> j 的边是否已经建立
int ind[1005]; 
int le[1005];//级别 void kahn(){queue<int> q;for(int i=1;i<=n;++i){if(ind[i]==0){q.push(i);le[i]=1;}}int x,y;while(!q.empty()){x=q.front();q.pop();for(int i=0;i<g[x].size();i++){y=g[x][i];le[y]=max(le[y],le[x]+1);ind[y]--;if(ind[y]==0){q.push(y);}}}
}int main(){cin>>n>>m;int sn;for(int i=1;i<=m;++i){cin>>sn;//第i趟线路停靠sn个站 memset(flag,0,sizeof(flag));for(int j=1;j<=sn;++j){cin>>s[j];flag[s[j]]=1;//s[j]站停靠了 }for(int j=s[1];j<=s[sn];j++){//枚举该线路中所有没停靠的站 if(flag[j]==1) continue;for(int k=1;k<=sn;k++){if(pd[j][s[k]]==0){//避免重复建边 g[j].push_back(s[k]);pd[j][s[k]]=1;ind[s[k]]++;}} }}kahn();int ans=-1;for(int i=1;i<=n;++i){ans=max(ans,le[i]);}cout<<ans;return 0;
} 

基于DFS的算法

时间复杂度:O(V+E)

直接求

#include<bits/stdc++.h>
using namespace std;
using ll=long long;//有向图------>保证是DAG图 无环 
int n,m;
struct Edge{//只求拓扑序列不用带边权 int to,next;
}e[10005];
int h[105];
int ind[105];//入度 
int cnt;
stack<int> topo; 
int vis[105];void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++; 
} void DFS(int x){vis[x]=1;int y;for(int i=h[x];~i;i=e[i].next){y=e[i].to;if(vis[y]==0){DFS(y);}}topo.push(x);//返回时入栈 
}int main(){cin>>n>>m;memset(h,-1,sizeof h);int u,v;for(int i=1;i<=m;++i){cin>>u>>v;add(u,v);ind[v]++;}for(int i=1;i<=n;++i){if(vis[i]==0){DFS(i);}}while(!topo.empty()){cout<<topo.top()<<" ";topo.pop();} return 0;
} 

可以判环

#include<bits/stdc++.h>
using namespace std;
using ll=long long;//有向图------>判环 
int n,m;
struct Edge{//只求拓扑序列不用带边权 int to,next;
}e[10005];
int h[105];
int ind[105];//入度 
int cnt;
stack<int> topo; 
int vis[105];
//vis[i]=0  还未走过/还未遍历过
//      =1  该点及其后面所有的点已经访问完了---->回溯时标记为1 
//      =2  这个点走过一次了 现在正在遍历该点后面的点---->第一次访问标记为2 
int flag=0;//标记是否有环 void add(int u,int v){e[cnt].to=v;e[cnt].next=h[u];h[u]=cnt;cnt++; 
} void DFS(int x){vis[x]=2;int y;for(int i=h[x];~i;i=e[i].next){y=e[i].to;if(vis[y]==2){flag=1;//ans=y;  //可以用ans来记录环从哪里开始 return ;} else if(vis[y]==0){DFS(y);if(flag==1){return ;}}}topo.push(x);//返回时入栈 vis[x]=1; //x及其后面的点访问结束 
}int main(){cin>>n>>m;memset(h,-1,sizeof h);int u,v;for(int i=1;i<=m;++i){cin>>u>>v;add(u,v);ind[v]++;}for(int i=1;i<=n;++i){if(vis[i]==0){DFS(i);}}if(flag==1){cout<<"有环"<<endl;return 0; } while(!topo.empty()){cout<<topo.top()<<" ";topo.pop();} return 0;
} 
http://www.lryc.cn/news/597118.html

相关文章:

  • CI/CD与DevOps集成方法
  • python在windows电脑找回WiFi密码
  • 【按下电源键后,电脑里发生了什么?——BIOS:启动世界的“第一把钥匙”】
  • C++编程学习(第14天)
  • [Mediatek] MTK openwrt-21.02 wifi 没启动问题
  • 详述消息队列kafka
  • 【通识】手机和芯片相关
  • LazyVim 加载顺序
  • MySQL金融级数据一致性保障:从原理到实战
  • 数据持久化--PlayerPrefs
  • Hexo - 免费搭建个人博客06 - 安装、切换主题Butterfly
  • 基于Java实现DFT、FFT,并绘制波形图和频谱图,音频播放频谱或波形图
  • 内积(Inner Product)和余弦相似度区别
  • MATLAB近红外光谱分析:MATLAB编程+BP神经网络+SVM+随机森林+遗传算法+变量降维+卷积神经网络等
  • 以 “有机” 重构增长:云集从电商平台到健康生活社区的跃迁
  • 零工合规挑战:盖雅以智能安全体系重构企业用工风控
  • 认识linux进程内存布局以及与命令行参数和环境变量的关系
  • 如何在VS code里使用SQLtool连接上WSL上的MySQL服务
  • 【软件系统架构】系列七:物联网云平台系统性能深入解析
  • 线性神经网络(深度学习-李沐-学习笔记)
  • 探索大语言模型(LLM):提升 RAG 性能的全方位优化策略
  • 我考PostgreSQL中级专家证书二三事
  • 论文略读:REMEDY: RECIPE MERGING DYNAMICS IN LARGE VISION-LANGUAGE MODELS
  • vue3笔记(2)自用
  • 微软2025教育AI报告:教育群体采用AI的比例显著提升
  • 技术速递|使用 Semantic Kernel 与 A2A 协议构建多智能体解决方案
  • Qt 样式表(QSS):打造个性化界面
  • 【前端】【Vue DevTools】Vue DevTools 进阶:用 Trae / Cursor 替换 VSCode 打开文件(跳转行列无误)
  • 论文略读:Knowledge is a Region in Weight Space for Finetuned Language Models
  • iOS上使用WebRTC推拉流的案例