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