1. 程式人生 > >【Vj作業】【拓撲排序經典理解題】Ordering Tasks 1、Kahn算法;2、基於DFS的算法。

【Vj作業】【拓撲排序經典理解題】Ordering Tasks 1、Kahn算法;2、基於DFS的算法。

pri from have for 失敗 dep empty enc there

2018-02-13

鏈接 https://cn.vjudge.net/contest/211129#problem/D

John has n tasks to do. Unfortunately, the tasks are not independent and the execution of one task is only possible if other tasks have already been executed.
Input
The input will consist of several instances of the problem. Each instance begins with a line containing two integers, 1 ≤ n ≤ 100 and m. n is the number of tasks (numbered from 1 to n) and m is the number of direct precedence relations between tasks. After this, there will be m lines with two integers i and j, representing the fact that task i must be executed before task j. An instance with n = m = 0 will ?nish the input.
Output


For each instance, print a line with n integers representing the tasks in a possible order of execution.
Sample Input
5 4 1 2 2 3 1 3 1 5 0 0
Sample Output
1 4 2 5 3

感想:

剛開始學拓撲排序,學了下面兩種方法去做這道經典的簡單題:1、Kahn算法;2、基於DFS的算法(用得比較多)。說實話,完全看懂花了一個晚上一個早上,實現基本上也是跟人家的實現沒差的,雖然很不喜歡這麽不獨立地做題,但是事實水平還不到,也是剛學嘛...什麽時候做什麽事,學習有方法,怎麽學有效率,還在摸索...據說兩者復雜度一樣都是O(V+E),之前對復雜度的計算有很多誤解,現在開始遇到一種算法就自己算一下,慢慢理解進去,也能培養做題前計算復雜度的習慣,同時方便以後做題。學得差不多了會把所有算法的復雜度自己整理一遍。

Kahn代碼:

     /*拓撲排序*/ 
#include<cstdio>               
#include<cstring>  
#include<vector>  
#include<queue>                   // 
using namespace std;  
const int N = 105;  
int n,m,son[N],topoSort[N],t;    //son是用來儲存入度的 toposort顧名思義就是存放題目要求的序列 
char O[2*N];  
vector<int>G[N];                 //
vector關聯頂點 G[N]接下來是用來斷弧的 void init(){ for(int i=1; i<=n; ++i){ G[i].clear(); } memset(son, 0, sizeof(son)); } int main(){ int u,v; while(scanf("%d%d",&n,&m)&&n+m){ init(); for(int i=0; i<m; ++i){ //這些地方i的初始值都不是隨意的,要小心,老是出錯 scanf("%d%d",&u,&v); G[u].push_back(v); ++son[v]; } int num=n; queue<int>q; // 先進先出,類似排隊那樣,先進去的元素最先出去的一種數據結構。 for(int i=1; i<=n; ++i){ if(!son[i]) q.push(i); //將0入度的點推入棧 } int pos=0; while(!q.empty()){ // empty 判斷容器是否為空,如果為空則返回true 這裏就是為非空的時候執行while int t=q.front(); q.pop(); topoSort[pos++] = t; for(int v=0; v<G[t].size(); ++v) //斷弧 註意寫法 if(--son[G[t][v]]==0) q.push(G[t][v]); //kahn的復雜度為O(n+m) 其實是n的線性階加上這裏去弧的次數 (也就是看最裏面的循環次數,這裏就是m) } for(int i=0; i<pos; ++i){ if(!i)printf("%d",topoSort[i]); else printf(" %d",topoSort[i]); } printf("\n"); } return 0; }

基於DFS的算法(運用遞歸,要判斷是否為DAG)

#include<string.h>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector> 
#include<queue>
using namespace std;
int n,m;
int vis[105];                 //標記
int topo[105]; 
int G[105][105];
int t;
bool dfs(int u)
{
    vis[u]=-1;                //表示正在訪問 1表示已經訪問過 0表示沒有訪問過
    for(int v=1;v<=n;v++)
    {
        if(G[u][v])      
        {
            if(vis[v]==-1) return false;         // 如果存在有向環,失敗退出
            if(!vis[v]&&!dfs(v)) return false; //!vis[v]是為了檢驗這個點是否已經被訪問過 訪問過就不dfs 不然會重復存入 實際效果就是省略一個if啦,想了好久唉 
        }                                        //以後可以這樣寫 
    }
    vis[u]=1; topo[--t]=u;
    return true;
}
bool toposort()
{   
    t=n;
    memset(vis,0,sizeof(vis));
    for(int u=1;u<=n;u++)
    {
        if(!vis[u]&&!dfs(u)) return false;//只有vis[u]是0時 進行dfs 而且存在環的時候才返回false   vis[u]=1說明已經訪問過 就不dfs了 避免改變vis的值
        
    }
    return true;
} 
int main()
{  
   int a,b;
   while(~scanf("%d%d",&n,&m)&&m+n) // 註意輸入那裏的結束條件不能是 n&&m,因為m可能是0
   {
         memset(G,0,sizeof(G));
         for(int i=1;i<=m;i++)
       {
         scanf("%d%d",&a,&b);
         G[a][b]=1; 
       } 
          if(toposort())
       {
             for(int i=0;i<n;i++)
             if(!i) printf("%d",topo[i]);
          else   printf(" %d",topo[i]);
          printf("\n");      
          }          
   }    
} 

【Vj作業】【拓撲排序經典理解題】Ordering Tasks 1、Kahn算法;2、基於DFS的算法。