拓扑排序和关键路径分析

肛了小一天的《离散数学》和《算法导论》,希望能深入浅出的说明这个本来就很简单的问题

拓扑排序是一个比较神奇的东西。

下面给出它的定义:

在集合论中它的定义是:构造一个包含某个给定偏序的全序的过程称为拓扑排序。(见《离散数学》P41)

在图论中的定义是:对于一个有向无环图G = (V,E),对于某条边(u,v)属于G ,则结点u在拓扑排序中处于结点v的前面。(见《算法导论》P355)

是不是有一点晦涩难懂呢?哈哈,找张图来说明吧

(谁画的图,好丑啊)

在这张图中,我们可以知道1->2,1->3,2->4,3->4;

但是,2和3之间的关系怎么确定呢?

简单啊,自己加一条边呗(嘿嘿嘿)

2->3,3->2都可以,喜欢那个用哪个

以 2->3为例 加上一条边之后,就成了这个样子:


之后 这个图就可以写成 1->2->3->4 了;

当然 因为加边的过程是任意的,你也可以写成 1->3->2->4;

这就是拓扑排序

是不是很简单呢?

个人理解:拓扑排序的意义就在于在一个有向图中,找出一个顺序,可以在有向图中,依次遍历各个点。

那我们应该怎么实现拓扑排序呢?

也很简单,首先,我们需要找到这个图中的“头”,也就是在图中,只向外指的点。例如图中的“1”,他就是这个图中的“头”;我们把1记录下来,然后砍掉他


现在出现了两个“头”,怎么办呢?随便砍一个吧!


砍了2之后,接下来的操作,想必你已经会了吧!接着砍砍砍……

直到没有头可砍,我们的任务就完成了。

如果觉得已经掌握这种算法了

那我就来介绍一个拓扑排序的应用——关键路径分析法(看离散知道的233)

大家可以想一下起床的过程——起床,穿衣,穿裤子,穿袜子,刷牙洗脸,煮饭,吃饭,出门。

有些事情的顺序是不能变的,比方说,我不能没穿衣服就出门;

但有一些顺序是可变的,比如煮饭可以先于刷牙洗脸,而且他们可以同时进行,这样会大大节省时间;

但是,不管我再怎么节省时间,时间还是在流逝。我要找我总共需要花费多少时间以及找出我为什么要花这么多时间(即找出一条关键路径来刻画我完成这些流程所需时间的过程)


在这里贴一道关键路径分析的经典题:hdu 4109(鶸用来一种很傻缺的思路写这个题,实在是太丑了233)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

queue<int> cost[1005];//存时间消耗
queue<int> son[1005];//存儿子
queue<int> H; //存头
int h[1005],e[1005];
int ans[1005];
int main() {
    int n,m;
    while(cin >> n >> m) {
        for (int i = 0; i < n; i++) {
            cost[i] = son[i] = queue<int>();
            h[i] = e[i] = ans[i] = 0;
        }
        while(m--) {
            int x,y,z;
             cin >> x >> y >> z;
             h[y]++;
             e[x] = 1;
             son[x].push(y);
             cost[x].push(z);
        }
        for (int i = 0; i < n; i++) {
            if(!h[i]) {
                H.push(i);
                ans[i] = 1;
            }
        }
        while(!H.empty()) {
            int tmp = H.front();
            while(!son[tmp].empty()) {
                int tmp1 = son[tmp].front();
                ans[tmp1] = max(ans[tmp1],cost[tmp].front() + ans[tmp]);
                h[tmp1]--;
                if(!h[tmp1])
               		H.push(tmp1);
                son[tmp].pop();
                cost[tmp].pop();
            }
            H.pop();
        }
        int Max = 0;
        for (int i = 0; i < n; i++) {
            if(!e[i]) { 
                Max = max(Max,ans[i]);
            }
        }
        cout << Max <<endl;
    }
    return 0;
}
/*
10 11
0 2 4
1 2 3
2 3 5
2 4 4
2 6 4
3 5 3
4 5 3
6 7 2
5 8 6
7 9 11
8 9 3
要是wa了就试一试这一组数据吧
*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值