AOV网
AOV网,英文(Activity On Vertex Network),是一种用顶点表示活动,有向边表示活动的先后关系的有向无环图。例如:某些课程,是一些课程学习的先决条件,”课程学习“ 这个活动构成严格偏序。
很明显AOV网中不应该有环,因为这样意味着一个活动进行的先决条件是自己,这样就出现了矛盾。
在有向无环图中,只有出边,没有入边(即入度为0 出度不为0)的顶点叫源点,只有入边,没有出边的点叫 汇点,容易证明有向无环图中一定存在源点和汇点,一会儿会用到这两个概念。
拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
这是百度百科对拓扑排序的定义,注意到 “拓扑排序是由某个集合上的一个偏序关系得到集合上的一个全序”。也就是说构成偏序的不仅仅可以是活动的先后关系,还可以是别的,比如 某些问题中的 “从属关系”,“大小关系”…。只要能表示为一个有向无环图的关系,都可以使用拓扑排序得到一个全序。
拓扑排序的算法流程
- 找到一个入度为0的结点(即一个源点),将结点插入队列(我习惯用队列,栈也可以,下面用队列做例子)保存在答案中。
- 在原图中删掉刚刚的源点, 和这个结点所有的出边,更新该结点的后继结点的入度。
- 重复1 和 2 并保存,同时记录保存结点的个数,直到原图中没有入度为0的结点。
- 检查保存结点的个数,如果小于结点的总数,则输出有环的信息,原图并非有向无环图,否则输出保存的答案结点序列。
int queue[MAXN] = {0}, head = 0, tail = 0; //队列,头尾指针
int ans[MAXN] = {0}, cnt = 0;
for(int i=1;i<=N;i++) //找初始的源点
{
if(in[i]==0)
{
tail++;
queue[tail] = i;
}
}
do
{
head++;
int now = queue[head];
ans[++cnt] = now; //保存答案
for(int i=1;i<=N;i++)
{
if(map[now][i]==1&&i!=now)
{
map[now][i] = 0; //删掉这条边
in[i]--; //更新入度
if(in[i]==0)
{
tail++;
queue[tail] = i;
}
}
}
}while(head<tail);
if(cnt!=n)
printf("Error\n"); //有环信息
由于前面已经说明了,有向无环图必然存在源点,若结束算法时,没有扫描到所有顶点,说明一部分结点构成的子图没有源点,则原图不是有向无环图。
由上面的流程可知,拓扑排序得到的序列可能不是唯一的,因为同一时间可能有多个入度为0的顶点,此时保存的顺序,如果不进行规定的话实际上是任意的。这衍生出了关于拓扑排序的很多问题。
关于拓扑排序的基本问题
- 求任意一个拓扑序列 / 求字典序最小的拓扑序列
前一个问题就是刚才写的正常流程,关键是求字典序最小的拓扑序列,这就要定义插入队列的优先级,可以用 C++ 的 优先队列priority_queue,也可以在了解其原理(堆排序)后手写优先队列,以结点编号为优先级。
代码:
for(int i=1;i<=N;i++) //找源点
{
if(du[i]==0)
Put(i); //小根堆的插入函数,具体实现请学习 “堆”
}
while(Hsize>0) //当队列非空
{
int now = Heap[1];
printf("%d", now);
cnt++;
if(cnt!=N)
printf(" ");
Delete(); //小根堆的删除函数
for(int i=1;i<=N;i++) //遍历所有结点寻找now的后继
{
if(map[now][i]==1) //如果i是now的后继
{
map[now][i] = 0; //删掉这条边
du[i]--; //更新入度
if(du[i]==0) //入度为0则入队
Put(i);
}
}
}
上面的代码把 “堆” 的部分去掉就是正常的拓扑排序。
- 求所有拓扑序列中,位置固定的元素。
在某些问题中,我们需要确定某个元素的排名。由于拓扑序列的不唯一,一些元素无法确定排名,一些元素可以确定一个唯一的排名,即这个元素在所有拓扑序列中的位置相同。
判断方法是:一个元素的名次确定,当且仅当同一时间只有该结点入度为0,且比该结点先入队的结点与该结点均可比。
理由:比确定排名结点更早入队的结点需要和该结点可比,以保证他们严格“大于” 该结点,如果同一时间有一个以上的入度为0的结点,容易证明这两个结点一定不可比,而且这些结点的入队顺序可以不同,所以这种情况是不会产生名次确定的结点的。
相关问题
拓扑排序模板: 洛谷P1113 杂务.
Floyed+拓扑排序: 洛谷P2419 USACO08JANCow Contest S.
拓扑排序+思维(NOIP普及组考试题): 洛谷P1983 车站分级.
真·拓扑排序 : 洛谷P1347 排序.