拓扑排序--入度

换个形象点的解释,我们在学习一门课程之前,应该需要一定的预备知识,比如在学习B课程之前我们需先学习A(后用< X,Y > 表示X课程是Y课程的预备知识,其实与上述有序偶的含义相同),则有 < A,B >。我们还有 < C,B >, < B,D >, < E,D >, < D,F >, < D,G >, < H,G >. 现在要求你合理安排A-H这些课程的学习顺序。这个任务的要求实际上就是对A-H进行拓扑排序。 

以上面给课程排序为例,我们首先要学的,一定是一个不需要任何预备知识的课程,然后学完这个课程之后,根据边的关系再看有哪些新的课程可以学习,同时我们还要清楚,学完一门课程后,就不需要再次学习这一门课程了

我们将其与图做个类比。 
不需要预备知识的课程-> 入度为0的点 
新的课程->所指向的下一个点 
每门课之学一次->从图中删除节点 && 从图中删除有向边 

接着再根据图类比结果决定存储的数据 
入度为0的点->需要存储每个节点的入度 
所指向的下一个点->需要存每个节点的后继 
删除节点与有向边-> 这里讨论一下: 
如果我们真的删除了节点和有向边,那就意味着无法在找到这些数据了。因为我们还需要判断是否形成了DAG,最保险的做法是将下一个节点的入度-1。如果发现某个节点的入度为-1了,表明存在有向环,那么说明不存在与拓扑排序。

hdu1285

要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。

#include <iostream>

#include <cstdio>

#include <vector>

#include <cstring>

#include <queue>

using namespacestd;

const int maxn =505;

int indegree[maxn];

vector<int> mp[maxn];

queue<int> q,ans;

void init(int n)

{

    memset(indegree,0,sizeof(indegree));

    for (int i =1; i <= n; i ++) {

        mp[i].clear();

    }

    while (!q.empty()) {

        q.pop();

    }

    while(!ans.empty())

    {

        ans.pop();

    }

}

bool solve(int n)

{

    bool succ =true;

    while(1)

    {

        for (int i =1; i <= n ; i ++) {

            if (indegree[i] ==0) {//每次寻找入度为0的点,即不用其他知识即可以直接学的,加入队列。

                q.push(i);

                ans.push(i);

                indegree[i] = -1;

                break;//该题要求输出编号小的在前,所以每次取编号最小的入度为0的点开始。若不要求,该行break可删除。

            }

        }

        if (q.empty()) {

            break;

        }

        while (!q.empty()) {

            int t =q.front();q.pop();

            for (int i =0; i <mp[t].size(); i ++) {

                if (indegree[mp[t][i]] == -1) {//如果下一个点入度为-1,说明在之前已经学过,形成了环,所以不能进行拓扑排序

                    succ = false;

                    break;

                }

                elseindegree[mp[t][i]] --;//每次去掉所指向的点的一条边,当一个点没有被指向,入度为0时,就可以被学习

            }

            mp[t].clear();

            if (!succ) {

                break;

            }

        }

        if (!succ) {

            break;

        }

    }

    if (ans.size() != n) {//

        succ = false;

    }

    return succ;

}

int main()

{

    int n,m;

    while (scanf("%d%d",&n,&m) !=EOF) {

        int a,b;

        init(n);

        for (int i =0; i < m; i ++) {

            scanf("%d%d",&a,&b);

            mp[a].push_back(b);

            indegree[b] ++;

        }

        if(solve(n))

        {

            printf("%d",ans.front()) ;ans.pop();

            while (!ans.empty()) {

               printf(" %d",ans.front());

                ans.pop();

                

            }

            printf("\n");

       }

        

    }

    return0;

}



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值