拓扑排序介绍,及深入了解(未完待续)

本文深入解析拓扑排序的概念,从偏序到全序的转换,以及如何通过算法判断图中有无环,提供了一系列由易到难的例题,并附带详细代码讲解。

简单介绍:

由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。离散数学中关于偏序和全序的定义:
若集合X上的关系是R,且R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
设R是集合X上的偏序(Partial Order),如果对每个x,y属于X必有xRy 或 yRx,则称R是集合X上的全序关系。
比较简单的理解:偏序是指集合中只有部分成员可以比较,全序是指集合中所有的成员之间均可以比较。

注意:

①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。
②若图中存在有向环,则不可能使顶点满足拓扑次序。
③一个DAG的拓扑序列通常表示某种方案切实可行。

实现过程:
1.在有向图中选一个没有前驱的顶点并且输出
2.从图中删除该定点和所有出边
3.重复上述两步直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此也可以通过拓扑排序来判断一个图是否有环。

1.什么是拓扑排序

百度解释:由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

我们可以简单的理解为,做一件事情前是要有先决条件的,比如,你吃饭的前提是要先做饭。睡觉的前提是A完题(.疯狂暗示)

如图此时没有先决条件的仅有1,所以可以将其移除

得到如图结果此时2无先决条件再将2移除,依次操作。

所以按照以上步骤,我们可以看到拓扑排序,就是按照先决条件的排序。而且如图所示。排序结果也不一定是唯一的,到3 4时均无先觉条件均可以输出。结果可能为1 2 3 4 5或1 2 4 3 5.

那怎样可以让他按最小字典序输出呢 方法:优先队列。

2.例题陈列(由易到难)

1.入门题

(1)hihocoder1174

(2)hihocoder1175

(3)HDU  1285  确定比赛名次 

(4)HDU  3342 Legal or Not 

(5)Uva  10305  Ordering Tasks  【easy】

2.上升题

(1)  POJ  3660 Cow Contest

(2)POJ  3553 Task schedule

(3)POJ  3687  Labeling Balls

(4)2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 Skiing

(5)D. Almost Acyclic Graph

3.苦难题

(1)Codeforces  512A  Fox And Names

(2)Codeforces  516B  Drazil and Tiles

(3)Codeforces  698B  Fix a Tree

(4)HDU  3231 Box Relations

(5)SDNU  1031  字母排序

3.例题讲解

(1)入门题目

1.hihocoder1174

题目简介:选课的拓扑排序,入门题目。判断有无环产生。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100100;

vector <int> G[maxn];
int grath[maxn], t, n, m;
void tosort()
{
    queue <int> que;
    int sum = 0;
    for(int i = 1; i <= n; i++) if(!grath[i]) que.push(i);
    while(!que.empty())
    {
        int a = que.front(); que.pop();
        sum++;
        for(int i = 0; i < G[a].size(); i++)
        {
            if(!(--grath[G[a][i]])) que.push(G[a][i]);
        }
    }
    if(sum == n)  // 无环,所有点都拆下来了
        printf("Correct\n");
    else
        printf("Wrong\n");
}
int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        memset(grath, 0, sizeof(grath));
        for(int i = 0; i <= n; i++) G[i].clear();
        for(int i = 0; i < m; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            grath[b]++;
        }
        tosort();
    }
    return 0;
}

2.入门题目hihocoder1175

题目简介:对拓扑排序加强的理解,病毒数的传播,

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int mod = 142857;

int n, m, k, grath[maxn], t[maxn], d;
vector <int> G[maxn];

void tosort()
{
    int sum = 0;
    queue <int> que;
    for(int i = 1; i <= n; i++) if(!grath[i]) que.push(i);
    while(!que.empty())
    {
        int a = que.front(); que.pop();
        sum = (sum + t[a]) % mod;
        for(int i = 0; i < G[a].size(); i++)
        {
            t[G[a][i]] = (t[G[a][i]] + t[a]) % mod;
            if(!(--grath[G[a][i]]))
            {
                que.push(G[a][i]);
            }
        }
    }
    printf("%d\n", sum % mod);
}
int main()
{
    while(scanf("%d%d%d", &n, &m, &k) != EOF)
    {
        for(int i = 0; i <= n; i++) G[i].clear(), grath[i] = 0, t[i] = 0;
        for(int i = 1; i <= k; i++) scanf("%d", &d), t[d]++;
        for(int i = 0; i < m; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            grath[b]++;
        }
        tosort();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值