[旧版][知识点]拓扑排序

// 此博文为迁移而来,写于2015年2月24日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vspe.html

 

Update - 20200607

该博文内容现已更新并迁移至:

[知识点] 8.4 拓扑排序与关键路径 https://www.cnblogs.com/jinkun113/p/13057781.html

1、前言
在了解拓扑排序之前,我们先来看一道题目以更好的理解:
 
       一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(activity)。在整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
 
         课程代号      课程名称        先修课程
           C1          高等数学             无
           C2          程序设计基础      无
           C3          离散数学             C1,C2
           C4          数据结构             C3,C5
           C5          算法语言             C2
           C6          编译技术             C4,C5
           C7          操作系统             C4,C9
           C8          普通物理             C1
           C9          计算机原理         C8
 
其AOV网如图所示:
 


2、什么是拓扑排序?
       一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行。一个具有三个顶点的回路,由边可得B活动必须在A活动之后,由边可得C活动必须在B活动之后,所以推出C活动必然在A活动之后,但由边可得C活动必须在A活动之前,从而出现矛盾,使每一项活动都无法进行。这种情况若在程序中出现,则称为死锁或死循环,是应该必须避免的。
       在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。例如,下面的三个序列都是上图的拓扑序列,当然还可以写出许多。
    (1) C1,C8,C9,C2,C3,C5,C4,C7,C6
    (2) C2,C1,C3,C5,C4,C6,C8,C9,C7
    (3) C1,C2,C3,C8,C9,C5,C4,C6,C7
 
3、有什么作用?
       除了上面我们之前我们展示的一道题目,拓扑排序的应用是比较广泛的。下面我们再看一个例子:
 
士兵排队 Soldier.cpp
<1>问题描述
       有N名士兵(1<=N<=26),编号依次为A,B,C,……进行队列训练时,指挥官要把一些士兵从高到矮依次排成一行,但现在指挥官不能直接获得每个士兵的身高信息,只能获得“P1比P2高”这样的比较结果(P1,P2∈{A,B,…Z},记为P1>P2),如“A>B”表示A比B高。编一程序,根据所得到的比较结果求出符合条件的排队方案。注:比较结果中没有涉及到的士兵不参加排队。
       例如,设有3个士兵,A、B、C,给出关系(A,B),(B,C)。其中(A,B)表示士兵A高于B,当上面的关系给出之后,可以将他们排成一队:ABC。
 
<2>输入
       通过文件输入,每个比较结果在文件中占一行。
<3>输出
       若输入数据无解或不能唯一确定,则输出“NO  ANSWER!”,否则从高到矮依次输出每一个士兵的编号,中间无分隔符,并把结果写入文本文件中。
 
<4>输入样例
A>B
B>F
F>D
<5>输出样例
ABFD
 
代码如下:
 
#include<cstdio>
#include<cstdlib>
#define MAXN 28
 
struct edge
{
  int next,v;
};
edge list[MAXN*2];
int head[MAXN],tot,n,have[MAXN],ListNum[MAXN],del[MAXN],get,ans[MAXN];
 
void AddList(char strU,char strV)
{
  int u=strU-64,v=strV-64;
  have[u]=have[v]=1;
  tot++;
  list[tot].v=v;
  list[tot].next=head[u];
  head[u]=tot;
  ListNum[v]++;
}
 
void init()
{
  char s[4];
  freopen("3.in","r",stdin);
  freopen("3.out","w",stdout);
  while (scanf("%s",s)!=EOF) AddList(s[0],s[2]);
  for (int i=1;i<=26;i++)
    if (have[i]==1) n++;
}
 
int check()
{
  for (int i=1;i<=26;i++)
    if (del[i]!=1 && have[i]==1) return 0;
  return 1;
}
 
void DFS(int now,int dep)
{
  del[now]=1;
  for (int x=head[now];x!=0;x=list[x].next)
    ListNum[list[x].v]--;
  ans[dep]=now;
  if (check()==1) 
  {
    if (get==1) { printf("NO ANSWER!"); exit(0); }
    else { get=1; return; }
  }
  for (int i=1;i<=26;i++)
    if (ListNum[i]==0 && del[i]!=1 && have[i]==1) DFS(i,dep+1);
  for (int x=head[now];x!=0;x=list[x].next)
    ListNum[list[x].v]++;
  del[now]=0;
}
 
 
int main()
{
  init();
  for (int i=1;i<=26;i++) 
    if (ListNum[i]==0 && have[i]==1) DFS(i,1);
  if (get==0) printf("NO ANSWER!");
  else for (int i=1;i<=n;i++) printf("%c",ans[i]+64);
  return 0;
}

 

原文地址:https://www.cnblogs.com/jinkun113/p/4677200.html