• 拓扑排序【Kahn算法(bfs)和dfs求拓扑序列及判环】


    拓扑排序

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,该排序满足这样的条件——对于图中的任意两个结点uv,若存在一条有向边从u指向v,则在拓扑排序中u一定出现在v前面。
     
    当且仅当一个有向图为有向无环图(DAG)时才存在对应于该图的拓扑排序。每一个有向无环图都至少存在一种拓扑排序。
     
    拓扑排序的实现:
    ①Kahn算法【常可用来判断该图是否是DAG(有向无环图)】
    算法复杂度为O(v+e)。
    算法实现:循环执行以下两步直到不存在入度为0的顶点为止。
    (1)选择一个入度为0的顶点并输出之;(2)从网中删除此顶点及其所有出边。
    循环结束后,若输出的顶点数小于网中的顶点数,则该图存在回路,否则输出的顶点序列就是拓扑序列,该图也即为DAG图。
     
    该算法可借助队列实现。类似于bfs:首先将入度为0的顶点入队,取出队头元素进行拓展,找其邻接点,每个邻接点的入度-1,当入度变为0时则入队。循环此操作直到队列为空。出队顶点序列即为拓扑序列。
    int k=0;
    void toposort()
    {
        queue<int>q;
        for(int i=1;i<=n;i++)
            if(!indegree[i])
                q.push(i);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            cout<<u<<' ';k++;//输出拓扑序列,计数+1
        for (u的每个邻接点v){ //vector或链式前向星建图,遍历方式不一样
            indegree[v]--;//删除边(u, v),即让v的入度-1;
            if (!indegree(v) )
                q.push(v);
          }
        }
    } 
    if (k!=n)
        存在环;
    else
        不存在环,为DAG图;   

    ②基于DFS的拓扑排序算法( 复杂度O(V+E) )

    1)DFS求给定DAG图的拓扑序列【前提:已知图是DAG】

    topo数组存求得的拓扑序列
    int k=0;
    void DFS(int x)
    {
        vis[x]=1;
        for(遍历x的邻接点j){
            if(!vis[j])
                DFS(j);
        }
        topo[k++]=x;//用栈存
    }
    void toposort()
    {
        for(int i=1;i<=n;i++){ //遍历每个顶点
            if(!vis[i])
                DFS(i);
        }
        for(int j=k-1;j>=0;j--)
            cout<<topo[i]<<' ';//逆序输出数组,所得序列即为拓扑序列
    }

    2)DFS判断有向图是否有环(是否存在合法的拓扑序列):

    对一个节点x进行dfs,判断是否能从x回到自己这个节点,即是否存在x到x的回路。

    这里需用一个color数组标记节点的状态:-1代表未访问,0代表正在被访问,1代表已被访问过

    #include<bits/stdc++.h>
    using namespace std;
    int n,e;//n个顶点(1<=n<=100),e条边
    int color[105];//color数组表示每个结点的状态
    vector<int>G[105];//vector邻接表建图
    bool flag;//为真则存在环
    void DFS(int x)
    {
        if(flag)//如果有环就返回,否者继续搜索
            return;
        color[x]=0;//x正在被访问,状态为0
        for(int i=0;i<G[x].size();i++){
            if(color[G[x][i]]==-1)//与x相连的结点状态为-1,则该节点未被访问,继续搜索
                dfs(G[x][i]);
            else if(color[G[x][i]]==0){//与x相连的结点状态也为0,代表有环,返回
                flag=true;
                return;
            }
        }
        color[x]=1;//标记x状态为已被访问过了
    }
    int main()
    {
        int u,v;
        while(~scanf("%d%d",&n,&e))
        {
            flag=false;
            for(int i=1;i<=n;i++)//二维vector初始化
                for(int j=0;j<G[i].size();j++)
                    G[i].pop_back();
            memset(color,-1,sizeof(color));//初始化为未访问
            
            for(int i=1;i<=e;i++){
                scanf("%d%d",&u,&v);
                G[u].push_back(v);
            }
            DFS(1);
            if(!flag)
                printf("有向图无环,存在合法的拓扑序列
    ");
            else 
                printf("有向图有环,不存在合法的拓扑序列
    ");
        }
        return 0;
    }

    UVA10305 给任务排序 Ordering Tasks

    题意

    John有n个任务要做,每个任务在做之前要先做特定的一些任务。

    输入第一行包含两个整数n和m,其中1<=n<=100。 n表示任务数,而m表示有m条任务之间的关系。 接下来有m行,每行包含两个整数i和j,表示任务i要在j之前做。

    当读入两个0(i=0,j=0)时,输入结束。

    输出包含q行,每行输出一条可行的安排方案。

    输入:
    5 4 1 2 2 3 1 3 1 5 0 0
    输出:
    1 4 2 5 3

    题解

    裸拓扑排序

    Code

    Kahn算法+vector邻接表

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    int n,m,indegree[105];
    void toposort(vector<vector<int> >G)
    {
        int space=0;
        queue<int>q;
        for(int i=1;i<=n;i++)
            if(!indegree[i])
                q.push(i);
        while(!q.empty()){
            int x=q.front();q.pop();
            space?printf(" "):printf(""),space=1;
            printf("%d",x);
            for(int j=0;j<G[x].size();j++){
                indegree[G[x][j]]--;
                if(!indegree[G[x][j]])
                    q.push(G[x][j]);
            }
        }
    }
    int main()
    {
        int u,v;
        while(~scanf("%d%d",&n,&m)&&n)
        {
            vector<vector<int> >G(n+5);//开n+5行的vector二维数组
            indegree[105]={};
            while(m--){
                scanf("%d%d",&u,&v);
                G[u].push_back(v);
                indegree[v]++;
            }
            toposort(G);
            puts("");
        }
        return 0;
    }

     基于DFS的拓扑排序+链式前向星建图

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<queue>
    using namespace std;
    struct node{
        int to,next;
    }G[105];
    int head[105],cnt;
    void add(int u,int v)
    {
        G[cnt].to=v;
        G[cnt].next=head[u];
        head[u]=cnt++;
    }
    int n,e,indegree[105],ans[105],k,vis[105];
    void DFS(int rec)
    {
        vis[rec]=1;
        for(int j=head[rec];~j;j=G[j].next){
            if(!vis[G[j].to])
                DFS(G[j].to);
        }
        ans[k++]=rec;
    }
    void toposort()
    {
        for(int i=1;i<=n;i++){
            if(!vis[i])DFS(i);
        }
        int q=1;
        for(;k>0;){
            q?cout<<"":cout<<" ";
            q=0;
            cout<<ans[--k];
        }
    }
    int main()
    {
        int u,v;
        while(~scanf("%d%d",&n,&e)&&n)
        {
            cnt=0;
            memset(vis,0,sizeof(vis));
            memset(head,-1,sizeof(head));
            for(int i=1;i<=e;i++){
                scanf("%d%d",&u,&v);
                add(u,v);
            }
            toposort();
            puts("");
        }
        return 0;
    }
     
  • 相关阅读:
    360随身WiFi使用问题解决,无法在没有网络的电脑上使用
    np问题(大数阶乘取模)
    传球(概率问题)
    区间平均值(逆序对)
    完全平方数最大
    计算毫秒
    祖玛游戏
    后缀最大值
    Blah数集(双指针单调队列)
    逢低吸纳(最长下降子序列+方案数+高精度)
  • 原文地址:https://www.cnblogs.com/HOLLAY/p/11776481.html
Copyright © 2020-2023  润新知