拓扑排序与VAS图

VAS图(我的理解)就是由一系列计划得关系得出的单项图例如c1->c2表示要进行c2必须要进行c1,描述完所有计划的先后关系后,也就得到了一张图,对这张VAS图进行排序就是拓扑排序,排序得结果就是所有计划进行得先后顺序,因为存在并列的计划,所以排序结果并不一定唯一。
思想就是利用图论得一些知识
先对队列进行初始化——循环遍历所有的点,如果发现点的入度为0的化就入队,由于题目大都会加上一个限制:尽量使得小的点在前,以确保答案唯一,所以采用了优先队列

for(int i = 1;i <= n;i++)
    {
        if(in[i] == 0)
        {
            myq.push(i);
        }
    }

接着就开始出队,并且,进行出对后的更新优化,出队一个点,把这个点所有的后继点的入度-1,如果发现入度因此为0的话,继续入队,直至队列为空

while(myq.size())
    {
        int a = myq.top();
        myq.pop();
        ret[len++] = a;
        for(int i = 0;i < nex[a].size();i++)
        {
            if(--in[nex[a][i]] == 0)myq.push(nex[a][i]);
        }
    }

上述的拓扑排序为正向的拓扑适用于HDU1285
而面对较为复杂的关系条件约束的时候,可能就需要逆向的思维了——逆向拓扑排序
说白了,正向的拓扑利用的是入度,一个一个的进出,而逆向的是利用出度,先确定尾巴,在确定头,例如HDU4857,它多了一个这样的约束条件:负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推。 ——意思大概是1的第二优先级最高,在符合第一优先级的基础上要先确保1在前面,才能去管2,3,……,所以,你再看看正向就不好说了,这里引用一下不吸血的Vampire大佬的博客
这里写图片描述
满足要求的拓扑序应该是:6 4 1 3 9 2 5 7 8 0。
如果利用正向的拓扑排序,由于对小数的照顾我们会先出3,就会错误了,一i那位我们要1尽可能的在前面,而如果1深深的藏在里面,我们正向去找就会非常的麻烦
引用:

让我们换个角度想一想,节点 3 和 6,应该是 6 先出队列,因为节点 1 在 6 的后面。这和节点 3 和 6 的编号大小没有任何关系。但是,再看另外两条路径的尾部,节点 2 和 8,可以肯定地说,2 一定先出队列,因为它们后面都没有别的节点了,这个时候完全以这两个节点本身的编号大小决定顺序。归纳起来就是说,对于若干条平行的路径,小的头部不一定排在前面,但是大的尾部一定排在后面。于是,就有了算法 3。
1. 把所有出度为 0 的节点放进优先队列 PQ
2. WHILE: PQ 不是空队列
3. 从 PQ 中取出编号最大的元素 a,把 a 添加到答案的头部。
4. FOR:所有指向 a 的边 b → a
5. 把 b 的出度减 1。如果 b 的出度变为 0,则把 b 放进优先队列PQ。
理解了上面的思路代码就可以独立的完成了,有点小激动,懂了之后,自己实现去,一次AC了

#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
#include <string.h>
using namespace std;
const int maxn = 1e5 + 1e4;
const int maxp = 3e4 + 3e3;
int out[maxp];
vector<int> pre[maxp];
int ret[maxp];
priority_queue<int> q;
void init(int n)
{
    memset(out,0,sizeof(out));
    for(int i = 1;i <= n;i++)
    {
        pre[i].clear();
    }
    while(q.size())q.pop();
}

void toposort(int n)
{
    for(int i = 1;i <= n;i++)
    {
        if(out[i] == 0)
        {
            q.push(i);
        }
    }
    int len = n;
    while(q.size())
    {
        int a = q.top();
        q.pop();

        ret[--len] = a;
        for(int i = 0;i < pre[a].size();i++)
        {
            if(--out[pre[a][i]] == 0)q.push(pre[a][i]);
        }
    }
}
int main()
{
    int t,n,m,a,b;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init(n);
        for(int i = 0;i < m;i++)
        {
            scanf("%d%d",&a,&b);
            out[a]++;
            pre[b].push_back(a);
        }
        toposort(n);
        for(int i = 0;i < n;i++)
        {
            if(!i)printf("%d",ret[i]);
            else printf(" %d",ret[i]);
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值