zoukankan      html  css  js  c++  java
  • 拓扑排序

    拓扑排序的两个算法:

    1. DFS

    执行一次DFS遍历,并记住顶点变成死端(即退出遍历栈)的顺序。将该顺序反过来就得到了拓扑排序的一个解。当然,在遍历的时候不能遇到回边。如果遇到一条回边,该图就不是无环有向图,并且对它顶点的拓扑排序是不可能的。

    难点:

    Q:如何实现“将该顺序反过来”?

    A:利用双端队列

    #include <deque>
    deque <int> 
    
    //in dfs():
    tp.push_front(u);
    
    //output:
    while(!tp.empty()){
        printf("%d ",tp.front());
        tp.pop_front();
    }

    代码实现(以UVa10305为例)

    伪代码:

    Adapted from: https://class.coursera.org/algo-009/lecture/52

    topo_sort()

    - mark all nodes as unvisited

    - for each vertex i∈G:

      - if i not yet visited:

        - dfs(i)

    dfs(int u)

    - mark u as currently being visited(-1)

    - for each edge (u,v):

      -if v is currently being visited:

        - cyclic!

      - if v not yet visited:

        - dfs(v)

    - put u at the front of the queue

    - mark u as already visited

    /*
        Source: UVa10305
        Status: AC
        Done: 25/11/2015
        Remarks: topological sort (dfs)
    */
    #include <cstdio>
    #include <deque>
    #include <cstring>
    using namespace std;
    int G[105][105],visited[105],n,m;
    deque <int> tp;
    void dfs(int u){
        int v;
        visited[u] = -1; //u is currently being visited
        for(v = 1; v <= n; v ++){
            if(G[u][v] == 1){
                if(visited[v] == -1)return; //cyclic 
                if(visited[v] == 0) dfs(v);
            }    
        }
        tp.push_front(u);
        visited[u]=1; // u is already visited
        return;
    }
    void topo_sort(){
        int i;
        memset(visited,0,sizeof(visited)); //mark all nodes as unvisited
        for(i = 1; i <= n; i ++){ //for each vertex in G
            if(visited[i] == 0)dfs(i); //if(u is not yet visited)
        }
    }
    int main(){
        int u,v,i,j;
        while(1){
            scanf("%d %d",&n,&m);
            if(n == 0 && m == 0)break;
            for(i = 1; i <= m; i ++){
                scanf("%d %d",&u,&v);
                G[u][v] = 1;
            }
            topo_sort();
            while(!tp.empty()){
                printf("%d ",tp.front());
                tp.pop_front();
            }
            printf("
    ");
            for(i=1;i<=n;i++)
                for(j=1;j<=n;j++)
                    G[i][j]=0;
        }
        return 0;
    }

    2. Kahn

    不断地做这样的一件事,在余下的有向图中求出一个源,它是一个没有输入边的顶点,然后把它和所有从它出发的边都删除。如果有多个这样的源,可以任意选择一个。如果这样的源不存在,算法停止,因为该问题是无解的。 

    盲点:

    1. current=1 (The importance of initialisation!)

    2. 不断将新数加入队列从而实现循环

    3. G[u][v]的判断(见注释)

    /*
        Source: UVa10305
        Status: AC
        Done: 25/11/2015
        Remarks: topological sort (Kahn)
    */
    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    int G[105][105],deg[105],ans[105],n,m,current=1;
    queue <int> tp;
    void topo_sort(){
        int i,j,v,u;
        for(i = 1; i <= n; i ++){
            if(deg[i] == 0){
                tp.push(i);    
            }
        }
        while(!tp.empty()){
            u=tp.front(); //u是当前的“根” 
            tp.pop();
            ans[current++]=u;
            deg[u] = -1;
            for(v = 1; v <= n; v ++){
                if(G[u][v] == 1){
                    deg[v] --;
                }
            }
            for(v = 1; v <= n; v ++){
                if(deg[v] == 0 && G[u][v]){ 
                                //G[u][v]判断v是否为u的子结点,避免把其他“根”重复加入队列 
                                //样例数据: now u=1 tp:4 若无此判断 4会再次被加入队列 
                    G[u][v] = 0;
                    tp.push(v);    
                }
            }
        }
        return;
    }
    int main(){
        int u,v,i,j;
        while(1){
            scanf("%d %d",&n,&m);
            if(n == 0 && m == 0)break;
            for(i = 1; i <= m; i ++){
                scanf("%d %d",&u,&v);
                G[u][v] = 1;
                deg[v]++;
            }
            topo_sort();
            for(i=1;i<=n;i++)
             printf("%d ",ans[i]);
            printf("
    ");
            memset(G,0,sizeof(G));
            memset(deg,0,sizeof(deg));
            memset(ans,0,sizeof(ans));
            current = 1;
        }
        return 0;
    }
  • 相关阅读:
    Objective-C 【self的用法】
    Objective-C 学习笔记(Day 3,下)
    Objective-C 学习笔记(Day 3,上)
    Objective-C 学习笔记(Day 2)
    牵扯较多属性和方法的类题目,很简单的题目本来不想发的,如果有同学学到这个题目感觉太长不愿敲代码,copy走我的即可~不过还是建议自己打一打
    Objective-C 学习笔记(Day 1)
    输出一个等边三角形的字母阵,等边三角形的两腰为字母A,往里靠依次字母大一个(详细题目文章中描述)
    分类或者叫类别 oc
    自定义构造方法 initWithName: oc
    self = [super init] 拿出来单独讲讲
  • 原文地址:https://www.cnblogs.com/peccavi/p/4994770.html
Copyright © 2011-2022 走看看