拓扑排序

原理

  • 一个无环的有向图被称为有向无环图。
  • 有向无环图是描述一个工程、计划、生产、系统等流程的有效工具。活动之间通常又一定的约束。例如先后顺序。
  • 用节点表示活动,用弧表示活动之间的优先关系的有向图,被称为AOV网。
  • 在AOV网中,若<i,j>是图中的弧,则称节点i是节点j的直接前驱,节点j是节点i的直接后继。
  • AOV网中的弧表示了活动之间存在的制约关系。
  • 拓扑排序指将AOV网中的节点排成一个线性序列,该序列必须满足:若从节点i到节点j有一条路径,则在该序列中节点i一定在节点j之前。
  • 拓扑排序的基本思想:①选择一个无前驱的节点并输出;②从图中删除该节点和该节点的所有发出边;③重复步骤1、2,直到不存在无前驱的节点;④如果输出的节点数少于AOV网中的节点数,则说明网中有环,否则输出的序列即拓扑序列。
  • 拓扑排序不是唯一的。

算法设计

  1. 求各节点的入度,将其存入数组indegree[]中,并将入度为0的节点入栈S。
  2. 如果栈不为空,则重复以下操作:①将栈顶元素i出栈并保存到拓扑序列数组topo[]中;②将节点i的所有邻接点入度都减1,如果减1后入度为0,则立即入栈S。
  3. 如果输出的节点数少于AOV网中的节点数,则说明网中有环,否则输出拓扑序列。

算法实现

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=105;
int map[maxn][maxn],indegree[maxn],topo[maxn];
int n,m;
stack<int>s;
bool TopoSort(); //拓扑排序
int main(){cin>>n>>m;memset(map,0,sizeof(map));memset(indegree,0,sizeof(indegree));for(int i=0;i<m;i++){int u,v;cin>>u>>v;map[u][v]=1; //节点u是节点v的直接前驱indegree[v]++;}TopoSort();for(int i=0;i<n-1;i++)cout<<topo[i]<<" ";cout<<topo[n-1]<<endl;return 0;
}bool TopoSort(){ //拓扑排序int cnt=0;for(int i=0;i<n;i++)if(indegree[i]==0) //将入度为0的节点入栈s.push(i);while(!s.empty()){ //栈不为空int u=s.top(); //栈顶元素出栈s.pop();topo[cnt++]=u;for(int j=0;j<n;j++)if(map[u][j])if(--indegree[j]==0) //减1后入度为0则入栈ss.push(j);}if(cnt<n) return 0;return 1;
}

输入:

6 8
0 1
0 2
0 3
2 1
2 4
3 4
5 3
5 4

输出:

5 0 3 2 4 1

训练1:家族树

题目描述

火星人的血缘关系制度令人困惑。在火星行星理事会中,令人困惑的家谱系统导致了一些尴尬;为了在所有讨论中不冒犯任何人,老火星人先发言,而不是年轻人或最年轻的无子女人员。但是,维护这个命令不是一项微不足道的任务,火星人并不会总是知道其父母和祖父母是谁,如果一个孙子先发言而不是其年轻的曾祖父先发言,则会出现错误。编写程序,保证理事会的每个成员都早于其每个后代发言。

输入:第1行包含整数N(1 \leq N \leq 100),表示火星行星理事会的成员数。成员编号为1 ~ N。接下来的N行,第i行包含第i个成员的孩子名单。孩子的名单可能是空的,名单以0结尾。

输出:单行输出一系列发言者的编号,用空格分隔。如果有几个序列满足条件,则输出任意一个,至少存在一个这样的序列。

算法实现

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=105;
int map[maxn][maxn],indegree[maxn],topo[maxn];
int n;
stack<int>s;
void TopoSort(); //拓扑排序
int main(){cin>>n;memset(map,0,sizeof(map));memset(indegree,0,sizeof(indegree));for(int i=1;i<=n;i++){int v;while(cin>>v&&v){map[i][v]=1;indegree[v]++;}}TopoSort();for(int i=1;i<n;i++)cout<<topo[i]<<" ";cout<<topo[n]<<endl;return 0;
}
void TopoSort(){ //拓扑排序int cnt=0;for(int i=1;i<=n;i++)if(indegree[i]==0)s.push(i);while(!s.empty()){int u=s.top();s.pop();topo[++cnt]=u;for(int j=1;j<=n;j++)if(map[u][j])if(--indegree[j]==0)s.push(j);}
}

输入:

5
0
4 5 1 0
1 0
5 3 0
3 0

输出:

2 4 5 3 1

训练2:全排序

题目描述

不同值的升序排序序列是使用某种形式的小于运算符从小到大排序的元素序列。例如,排序后的序列ABCD表示A<B,B<C和C<D。给定一组A<B形式的关系,要求确定是否指定已排序的订单。

输入:输入包含多个测试用例。每个测试用例的第1行都包含两个正整数n(2≤n≤26)n(2 \leq n \leq 26)n(2≤n≤26)和mmm。nnn表示要排序的对象数量,排序的对象是大写字母的前nnn个字符。mmm表示将给出的A<BA<BA<B形式的关系的数量。接下来的mmm行,每行都包含一种由3个字符组成的关系;第1个大写字母。字符“<”和第2个大写字母。n=m=0n = m = 0n=m=0的值表示输入结束。

输出:对于每个问题实例,其输出都由一行组成,该行应该是以下三种之一。

  • 在x种关系之后确定的排序顺序:yyy…y。
  • 无法确定排序顺序
  • 在x种关系后发现不一致。

算法设计

  1. 如果入度为0的节点个数为0,则说明有环;如果拓扑序列节点数小于n,则也说明有环。此情况即无序。
  2. 如果入度为0的节点个数大于1,则无法确定,因为拓扑序列不唯一。
  3. 否则是拓扑有序的,输出拓扑序列。
  4. 注意:①得到判断结果后不能break,需要继续输入,否则下一个测试用例会读入本次输入的剩余数据;②在数据输入完毕后才能判断是不是无法确定

算法实现

#include<iostream>
#include<cstring>
#include<string>
#include<stack>
using namespace std;
int map[27][27],indegree[27],topo[27],temp[27];
int n,flag;//flag=1:有序 flag=-1:不确定
stack<int>s;
int TopoSort(int n);
int main(){int m,n;bool sign;//当sign=1时,已得出结果string str;while(cin>>n>>m){if(m==0&&n==0) break;memset(map,0,sizeof(map));memset(indegree,0,sizeof(indegree));sign=0;for(int i=1;i<=m;i++){cin>>str;if(sign) continue; //一旦得出结果,对后续的输入不做处理int x=str[0]-'A'+1;int y=str[2]-'A'+1;map[x][y]=1;indegree[y]++;int s=TopoSort(n);if(s==0){ //有环printf("Inconsistency found after %d relations.\n",i);sign=1;}else if(s==1){ //有序printf("Sorted sequence determined after %d relations: ",i);for(int j=0;j<n;j++)cout<<char(topo[j]+'A'-1);printf(".\n");sign=1;}}if(!sign) //不确定printf("Sorted sequence cannot be determined.\n");}return 0;
}
int TopoSort(int n){ //拓扑排序flag=1;for(int i=1;i<=n;i++)temp[i]=indegree[i];//一边输入一边拓扑排序,所有入度数组不能改变int m=0,cnt=0;for(int i=1;i<=n;i++)//查找入度为零的顶点个数,若>1,拓扑序不确定if(temp[i]==0){s.push(i);cnt++;}if(cnt==0) return 0; //有环if(cnt>1) flag=-1; //不确定while(!s.empty()){cnt=0;int i=s.top();s.pop();topo[m++]=i;for(int j=1;j<=n;j++)if(map[i][j]){temp[j]--;if(!temp[j]){ //若入度为0,则入栈ss.push(j);cnt++;}}if(cnt>1) flag=-1;//不确定}if(m<n)//有环return 0;return flag;
}

输入:

4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0

输出:

Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.

训练3:标签球

题目描述

有N个不同重量的球,重量为1~N个单位。对球从1到N进行标记,使得:①没有两个球具有相同的标签;②标签满足几个约束,例如“标签为a的球比标签为b的球轻”。

输入:第1行包含测试用例的数量。每个测试用例的第1行都包含两个整数N(1 \leq N \leq 200)和M(0 \leq M \leq 40000),分别表示球的数量和约束的数量。后面的M行,每行都包含两个整数a和b,表示标签为a的球比标签为b的球轻。在每个测试用例前都有一个空行。

输出:对于每个测试用例,都单行输出标签1~N的球的重量。如果存在多种解决方案,则输出标签为1的球的最小重量,然后输出标签为2的球的最小重量,依次类推…如果不存在解,则输出-1。

算法设计

  • 可以采用两种方法解决:
  1. 建立正向图。i = n…1,j = n…1,检查第1个出度为0的点t,分配重量w[t] = i,将弧尾节点的出度减1,继续下一个循环。若没有出度为0的节点,说明有环,退出。
  2. 建立原图的逆向图。i = n…1,j = n…1,检查第1个入度为0的节点t,分配重量w[t] = i,将其邻接点的入度减1,继续下一个循环。若没有入度为0的节点,则说明有环,退出。

算法实现

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=205;
int map[maxn][maxn],in[maxn],w[maxn];
int n,m,T,u,v;
bool flag;
void TopoSort(); //拓扑排序
int main(){cin>>T;while(T--){memset(map,0,sizeof(map));memset(in,0,sizeof(in));cin>>n>>m;for(int i=1;i<=m;i++){cin>>u>>v;if(!map[v][u]){//建立逆向图,检查重复边 map[v][u]=1;in[u]++;}}TopoSort();if(flag){cout<<-1<<endl;continue;}for(int i=1;i<n;i++)cout<<w[i]<<" ";cout<<w[n]<<endl;}return 0;
}
void TopoSort(){ //拓扑排序flag=0;for(int i=n;i>0;i--){int t=-1;for(int j=n;j>0;j--)if(!in[j]){t=j;break;}if(t==-1){//有环flag=1;return;}in[t]=-1;w[t]=i;for(int j=1;j<=n;j++)if(map[t][j])in[j]--;}
}

输入:

54 04 1
1 14 2
1 2
2 14 1
2 14 1
3 2

输出:

1 2 3 4
-1
-1
2 1 3 4
1 3 2 4

训练4 秩序

题目描述

给定x<y形式的变量约束列表,编写程序,输出与约束一致的变量的所有顺序。例如,给定约束x<y和x<z,变量x、y和z的两个排序与这些约束一致:xyz和xzy。

输入:输入由一系列约束规范组成。每个约束规范都有两行组成:一行为变量列表,后面一行为约束列表。约束由一对变量给出,其中x y表示x<y。所有变量都是单个小写字母。在约束规范中至少有两个且不超过20个变量,至少有一个且不超过50个约束,至少有一个且不超过300个与约束规范中的约束条件一致的顺序。

输出:对每个约束规范,都以字典顺序单行输出与约束一致的所有排序。不同约束规范的输出以空行分隔。

算法设计

  1. 将变量列表的字符转换为数字并统计出现次数,累计变量列表的长度。
  2. 将每队约束都转换为数字,用邻接矩阵存储并统计入度。
  3. 以回溯法求解所有拓扑序列并输出。

算法实现

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxn=50;
int map[maxn][maxn],in[maxn],s[maxn];//邻接矩阵,入度,标记出现
int ans[maxn];
string str,ord;//字符串,秩序
int len,num;//字符串长度,秩序长度
void dfs(int t); //回溯法找所有的拓扑序列
int main(){while(getline(cin,str)){memset(map,0,sizeof(map));memset(in,0,sizeof(in));memset(s,0,sizeof(s));len=str.length();int i,j=0;for(i=0;i<len;i++){if(str[i]!=' '){s[str[i]-'a']++;j++;}}len=j;getline(cin,ord);num=ord.length();for(i=0;i<num;i+=2){int u=ord[i]-'a';i+=2;int v=ord[i]-'a';map[u][v]=1;in[v]++;}dfs(0);cout<<endl;}return 0;
}
void dfs(int t){ //回溯法找所有的拓扑序列 if(t>=len){for(int i=0;i<len;i++)cout<<char(ans[i]+'a');cout<<endl;}for(int i=0;i<26;i++){if(!in[i]&&s[i]){s[i]--;for(int j=0;j<26;j++)if(map[i][j])in[j]--;ans[t]=i;dfs(t+1);for(int j=0;j<26;j++)//回溯还原现场 if(map[i][j])in[j]++;s[i]++;}}
}

输入:

a b f g
a b b f
v w x y z
v y x v z v w v

输出:

abfg
abgf
agbf
gabfwxzvy
wzxvy
xwzvy
xzwvy
zwxvy
zxwvy

算法训练营 图的应用(拓扑排序)相关推荐

  1. 算法高级(33)-拓扑排序-maven依赖关系的确定

    一.拓扑排序(Topological Sorting) 1.定义 拓扑排序是一种图论算法,该算法在<数据结构与算法>一书中有涉猎.引用维基百科的定义:在图论中,由一个有向无环图的顶点组成的 ...

  2. java 有向无环图 树_拓扑排序-有向无环图(DAG, Directed Acyclic Graph)

    条件: 1.每个顶点出现且只出现一次. 2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面. 有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说. 一 ...

  3. 有向无环图的所有拓扑排序

    有向无环图的所有拓扑排序 对有向无环图DAG的拓扑排序是顶点的线性排序,从而使每一有向边[u,v][u,v][u,v],顶点u进来的顺序v在.如果图不是 DAG,则无法对图进行拓扑排序. 给定一个 D ...

  4. 图的应用 | 拓扑排序

    拓扑序不唯一 一个图存在拓扑序  等价于 该图为有向无环图 1.有向图的拓扑排序实现: 辅助数据结构: 记录入度的数组 indegree[]: 初始化:创建的时候同步初始化: 队列zero:记录当前入 ...

  5. 图论--拓扑排序--判断一个图能否被拓扑排序

    拓扑排序的实现条件,以及结合应用场景,我们都能得到拓扑排序适用于DAG图(Directed Acyclic Graph简称DAG)有向无环图, 根据关系我们能得到一个线性序列,实现的方式是DFS,具体 ...

  6. 图综合练习--拓扑排序_03 数据结构与算法 - 排序

    1. 冒泡排序 Bubble Sort 基本思想 给定一个数组,这些元素将通过相互之间的比较,按照大小顺序一个个地像气泡一样浮出水面 实现 每一轮,从头部开始,每两个元素比较大小进行交换,直到这一轮中 ...

  7. LeetCode 207. Course Schedule--有向图找环--面试算法题--DFS递归,拓扑排序迭代--Python

    题目地址:Course Schedule - LeetCode There are a total of n courses you have to take, labeled from 0 to n ...

  8. 【数据结构-图】4.拓扑排序和关键路径(注解+原理)

    一.拓扑排序 1.1 基本知识 有向无环图:一个有向图中不存在环,简称DAG图 AOV网:用DAG图表示一个工程,其顶点表示活动,用有向边 <Vi,Vj><V_i, V_j>& ...

  9. 图综合练习--拓扑排序_拓扑排序

    一个场景 在大学里,每当到了期末的时候,你一定会头疼于选课给你带来的困扰.其中一项就是先修课的问题,每次你高高兴兴地选了自己心仪的选修课时,却发现自己不满足先修课的要求,只好默默地退掉.到底怎样安排我 ...

  10. 有向无环图中的拓扑排序

    ´有向无环图(DAG),指不存在环的有向图 ´点的入度,指以这个点为结束点的边数 ´点的出度,指以这个点为出发点的边数 ´拓扑序就是对于节点的一个排列使得若(u,v)∈E,那么u在排列中出现的位置一定 ...

最新文章

  1. 全面梳理关系型数据库和 NoSQL 的使用情景
  2. [JDBC] 获取数据库连接方式
  3. C++用法的学习心得
  4. order by case when
  5. 【转】CMake Error: The current CMakeCache.txt directory CMakeCache.txt is different than the directory
  6. ssh 登陆错误后禁止ip再次登陆_macOS破坏SSH默认规则,程序员无法登录Web服务器...
  7. 【转】Maven实战(八)---模块划分
  8. python中seaborn_python的seaborn模块
  9. linux装python环境_第一章 linux安装及python环境搭建
  10. 数字孪生智慧监狱三维可视化系统建设方案
  11. excel导入数据到mysql数据库
  12. 零信任嵌入式安全沙箱技术,企业应用软件的技术底座
  13. 论文阅读笔记(十三)——利用卷积神经网络进行农场猪脸识别
  14. 如何用一个makefile编译多个目标
  15. 怎么做新闻软文推广?故事性新闻稿写作技巧_云媒易
  16. Keil5 MDK版本使用ST-LINK下载程序的方法及注意事项
  17. 基础计算机教学论文,基础计算机论文,关于民办院校计算机基础课程实践教学体会相关参考文献资料-免费论文范文...
  18. 0PP0升级android版本,oppo哪些手机可以升级安卓11 oppo手机升级安卓11方法
  19. 智慧CAD手机看图隐私政策
  20. 网络字节序和主机字节序

热门文章

  1. 上兴远程控制使用及分析
  2. html转pdf加密文件,加密文件怎么解密?pdf加密文件去除密码
  3. ssm框架体检管理系统源码+文档
  4. Playmaker与iTween
  5. 百度开放平台Demo提示“Key验证失败...”的问题
  6. 如何测试电脑网速情况
  7. sklearn 1.0.1官方文档教程
  8. 高拍仪、浏览器获取获取多摄像头,切换摄像头
  9. Java —— EJB 到底是什么
  10. Win10桌面极简美化