算法学习笔记(拓扑排序)

拓扑排序

拓扑排序,在我现在看来,就是用来解决一系列分层次执行的问题。什么意思呢?举个例子(oi-wiki上看到的),比如说我们大学生都要面临选课问题,那么某些课程会有一些先行课程,必须先修这些先行课程才能够继续修读某课程,那么现在问题来了,小明有n种课程需要选读,n种课程之中,有一些课程有先后关系(也就是说修读某课程之前必须修它的先修课程,当然如果这个课程没有先修课程那就可以直接修读)小明每个学期都可以修若干门课程,但是只能修读那些没有先行课程或者先行课程已经在之前的学期中修读的课程(有点绕),问他修完这n门课至少需要多少个学期?或许此时你已豁然开朗想出一套算法并迅速实现,那请你关掉这个页面,或许你毫无头绪不知道怎么解决

其实可能接触过图论的人会想到,建图连边,一个结点如果没有入度那不就可以修了吗?

确实这种想法很正确,接下来说说拓扑排序的常见实现:

首先,我们用一个队列维护

我们先把入度为0的结点放入队列中,然后把这个队列中结点连出去的边删掉

再依次类推。代码实现很简单,我们在建图的时候就可以统计一下入度了,然后删边其实就是找到边的另一个端点,把它的入度--就可以了

By the way,个人图论很喜欢用链式前向星建图,不喜勿喷

喜闻乐见代码展示

 

void topo(){
    queue<int> q;
    for(int i = 1;i <= n;i ++)if(!in[i]) q.push(i);
    while(!q.empty()){
        int u = q.front();q.pop();
        for(int i = 1;i <= n;i ++){
            int v = e[i].to;
            in[v] --;//
            if(!in[v]) q.push(v);
        }
    }
}

 

但是这样子统计不了层数呀

所以我们也可以换个写法,用栈去维护

do{
        top = 0;
        for(int i = 1;i <= n;i ++){
            if(!in[i] && !fl[i]){//fl表示每个点只能进栈一次
                stk[++ top] = i;fl[i] = true;
            }
        }

        for(int i = 1;i <= top;i ++){
            for(int j = 1;j <= n;j ++){
                if(e[stk[i]][j]) e[stk[i]][j] = 0,in[j] --;
            }
        }
        ans ++;
    }while(top);

做两道好题试试水

https://www.luogu.com.cn/problem/P1983

https://www.luogu.com.cn/problem/P1038

 

posted @ 2021-03-11 21:13  zydbk  阅读(78)  评论(0)    收藏  举报