紫书第六章-----数据结构基础(拓扑排序)

说明:本文章参考刘汝佳《算法竞赛入门经典》(第2版)

拓扑排序

关于拓扑排序最经典的问题应该是排课程的问题,在此不再赘述。举一个例子,已知a<b,c<b,d<b,则a,b,c,d从小到大的顺序可能是a,c,d,b,也可能是a,d,c,b等其他情况,拓扑排序就是只需要给出任意一种情况即可。我们可以把上面的小于号当成图的有向边,a,b,c,d当成图的结点,就可以转换成有向图的问题了。很显然,如果图中存在有向环,则不存在拓扑排序,反之,则存在。不含有向环的有向图成为有向无环图(Directed Acyclic Graph, DAG)。 

例题 确定比赛名次 HDU - 1285

【代码】

#include<iostream>
#include<cstring>

using namespace std;

const int maxn=500+5;

int n,m;
int graph[maxn][maxn];
int indegree[maxn];
int topo[maxn];
int x,y;

bool toposort(){
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(indegree[j]==0){
                topo[cnt++]=j;
                indegree[j]--;
                for(int k=1;k<=n;k++){
                    if(graph[j][k]==1){
                        graph[j][k]=0;
                        indegree[k]--;
                    }
                }
                break;//注意此处有break,此break可以保证较小编号在前(例子:1 3,3 4,2 4
                    //若无break,拓扑序:1 3 4 2,有break,拓扑序:1 2 3 4
            }
        }
    }
    //判断是否有环
    for(int i=1;i<=n;i++){
        if(indegree[i]==0) return false;//上面进行了n次,还有度为0的点,说明有环
    }
    return true;
}

int main()
{
    while(cin>>n>>m){
        if(n==0) break;
        memset(graph,0,sizeof(graph));
        memset(indegree,0,sizeof(indegree));
        for(int i=1;i<=m;i++){
            cin>>x>>y;
            if(!graph[x][y]) indegree[y]++;//防止重边输入,很坑
            graph[x][y]=1;
        }
        toposort();
        int first=1;
        for(int i=0;i<n;i++){
            if(first){
                cout<<topo[i];
                first=0;
            }
            else
                cout<<" "<<topo[i];
        }
        cout<<endl;
    }
    return 0;
}

例题 Ordering Tasks UVA - 10305

【代码一】
类似上题代码
【代码二(dfs版本)不能保证较小编号的在前面】

#include<iostream>
#include<cstring>

using namespace std;

const int maxn=100+5;

int n,m;
int graph[maxn][maxn];
int vis[maxn];//-1代表正在访问,0代表未访问,1代表已经访问过
int topo[maxn];
int x,y;
int cnt;

bool dfs(int v){
    vis[v]=-1;
    for(int i=n;i>=1;i--){
        if(graph[v][i]){
            if(vis[i]<0) return false;//存在有向环
            else if(!vis[i] && !dfs(i)) return false;//递归看看子孙中是否有有向环
        }
    }
    vis[v]=1;topo[--cnt]=v;
    return true;
}

bool toposort(){
    memset(vis,0,sizeof(vis));
    for(int i=n;i>=1;i--){
        if(!vis[i])
            if(!dfs(i))
                return false;
    }
}

int main()
{
    while(cin>>n>>m){
        if(n==0) break;
        memset(graph,0,sizeof(graph));
        for(int i=1;i<=m;i++){
            cin>>x>>y;
            graph[x][y]=1;
        }
        cnt=n;
        toposort();
        int first=1;
        for(int i=0;i<n;i++){
            if(first){
                cout<<topo[i];
                first=0;
            }
            else
                cout<<" "<<topo[i];
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值