拓扑排序(Topological Order)UVa10305 Ordering Tasks

2016/5/19 17:39:07

拓扑排序,是对有向无环图(Directed Acylic Graph , DAG )进行的一种操作,这种操作是将DAG中的所有顶点排成一个线性序列,使得图中的任意一对顶点u,v满足如下条件:

若边(u,v)∈E(G),则在最终的线性序列中出现在v的前面

 

好了,说人话:拓扑排序的应用常常和AOV网相联系,在一个大型的工程中,某些项目不是独立于其他项目的,这意味着这种非独立的项目的完成必须依赖与其它项目的完成而完成,不妨记为u,v,则若边(u,v)∈E(G),代表着必须在项目u完成后,v才能完成。

这样,拓扑排序就可以表示一个工程的进度安排,这也样也更加方便我们理解为什么一个图如果有环,就一定不存在拓扑排序因为这相当于在工程中你不停的重复做同一个项目,工程变成一个项目的循环,自然不存在拓扑排序。

当然,拓扑排序往往不会只有一种,通过DFS,我们可以求得拓扑排序。

拓扑排序的思路简述如下:

  1. 状态标记:共三种,-1表示访问中,0表示未访问,1表示已访问,由数组c保存
  2. dfs终止的判别条件:如果存在环,则不存在,退出;反之把当前结点加入拓扑排序的首部(线性序列的当前第一个位置,随着排序的进行,这个位置会不断前移)
  3. 通过topo数组记录拓扑排序

这里解释一下书上的问题:为什么访问完一个节点就把当前结点加入到拓扑排序首部?

  答:因为由拓扑排序的性质可知,在DAG中,不妨任取从u顶点出发进行DFS,遇到v顶点,在最终的拓扑排序中始终应满足u在v之前,而根据DFS满足栈的FIFO性质可知,顶点v会先进入拓扑序列,顶点u后进入拓扑序列,因此,如果我们想要顺序获取拓扑序列,就应该将当前顶点(u)加入到拓扑排序的首部。当然,我们也可以通过模拟栈的FIFO特性,通过弹栈将逆序的拓扑序列变为顺序,但是这样无疑增加的操作的步骤,本质上是一样的。

接下来给出拓扑排序的代码:(图通过邻接矩阵存储)

 1 #include<cstdio>
 2 #include<cstring>
 3 const int maxn=100;
 4 int n,m,u,v,t,topo[maxn],G[maxn][maxn],c[maxn];
 5 //DFS
 6 bool dfs(int u){
 7     c[u]=-1;//正在访问中
 8     for(int i=0;i<n;i++)if(G[u][i]){
 9         if(c[i]<0)return false;//存在环,退出
10         if(!c[i]&&!dfs(i))return false;//i没访问过且访问后发现有环,退出 
11     }
12     c[u]=1;topo[--t]=u;//u顶点访问完毕,修改状态,加入拓扑序列首部
13     return true; 
14 }
15 //topological ordering
16 bool toposort(){
17     t=n;
18     memset(c,0,sizeof(c));
19     for(int i=0;i<n;i++)
20         if(!dfs(i))return false;
21     return true;
22 }

好了,介绍完拓扑排序,照例是大餐,算法和OJ搭配食用,味道更棒哦~

传送门:

UVa:10305 Ordering Tasks   https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=838&page=show_problem&problem=1246

这是一道非常简单的拓扑排序,由题意我们可以知道,我们无需判断环的存在,因此代码简洁了不少,不过,题目中要求m和n同时为0时代表结束,一开始写成了m&&n,导致wa,后来发现应该是m||n

另外,在解题时,我用了栈来简化逆序操作,不然我还要计算一下输出序列的长度,再把dfs的结果加入到拓扑排序首部,想想就觉得很麻烦,还不如自己写一个栈来的简单~~~

几个地方在提交的时候要注意:

  1. index作为全局变量,在本地编译可过,UVa上会complication error,所以这里改用了pos来指代栈指针
  2. 注意这是多输入的题目,所以vis数组一定要用memset清空
  3. 本来想把结果输出的printf整合为一句话,但是发现这样的话,换行符无法输出,我想过一段时间找点资料,了解一下printf的实现,就应该能明白问题的根源了。

代码如下:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define N 100
 4 int g[N+1][N+1],u,v,n,m,stack[N+1],vis[N+1],pos=0;
 5 void push(int x){
 6     stack[pos++]=x;
 7 }
 8 int pop(){
 9     return stack[--pos];
10 }
11 void dfs(int u){
12     vis[u]=-1;//visting
13     for(int i=1;i<=n;i++)if(g[u][i]&&!vis[i])
14         dfs(i);
15     push(u);vis[u]=1;//visted
16 } 
17 int main(){
18     while(scanf("%d%d",&n,&m)==2&&(m||n)){//这里是m或n,与的话会wa,我离散数学要去面壁 
19         while(m--){
20             scanf("%d%d",&u,&v);
21             g[u][v]=1;
22         }
23         memset(vis,0,sizeof(vis));//initialization
24         for(int i=1;i<=n;i++)//topological sort
25             if(!vis[i])dfs(i);
26         while(pos){//print result
27             printf("%d",pop());
28             printf("%c",pos>0?' ':'
');//三目运算符真的很好用
29         }
30     }
31 }
View Code

这一篇就到这里啦~~~

原文地址:https://www.cnblogs.com/luruiyuan/p/5509523.html