拓扑排序精讲

本文深入浅出地介绍了拓扑排序算法,通过实例讲解了如何使用该算法解决工程任务调度问题,展示了算法的具体实现过程,并提供了C++代码模板。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景引入

先看看一道题吧 (题目链接

与之相似的,在日常生活中,一项大的工程可以看作是由若干个子工程组成的集合,这些子工程必须在其他一些子工程完成之后才能开始,问这个总工程完成花费的最少时间。

求解这类问题就需要用到我们今天要讲的拓扑排序

算法讲解

其实思想很简单:

  1. 准备工作:读入数据时,如果做B的前提是先做完A,那么就从A向B连一条有向边;
  2. 读入完数据后,选择一个入度为0的点(因为入度为0说明做它没有前提条件,可以第一个做);
  3. 然后从图中删除此顶点及以此顶点为起点的所有关联边;
  4. 重复步骤2、3,直到不存在入度为0的顶点为止;
  5. 最后如果还存在点,那么这些点必定组成了一条(或多条)环,那这项工程就无法完成,有回路。否则输出的顶点的顺序就是一种拓扑排序。

好了,好有点儿昏?正常,本人表述不清,看看代码就好!(有详细的注释哦!)

Code(模板题

#include<bits/stdc++.h>
using namespace std;

queue<int>q;

const int MAX=1e4+10;

int n,Time[MAX],mx[MAX],ru[MAX],ans;
int tot,u[2*100*MAX],v[2*100*MAX],first[MAX],nxt[2*100*MAX];

void add(int a,int b)//构造邻接表模板,u为各边起点,v为各边终点
{
	tot++;
	u[tot]=a; v[tot]=b;
	nxt[tot]=first[u[tot]];
	first[u[tot]]=tot;
}

void topology()
{
	for(int i=1;i<=n;i++)//初始状态下,先做入度为0的
	{
		if(ru[i]==0)
		{
			q.push(i);//这里使用队列作为数据结构
			mx[i]=Time[i];//mx数组记录做完每件总共要花的时间
		}
	}
	while(!q.empty())//当队列中还有点要处理
	{
		int t=q.front();//取队列中第一个入度为0的点
		q.pop();
		for(int k=first[t];k;k=nxt[k])//枚举这个点为起点的每条边
		{
			ru[v[k]]--;//处理完一条边,这条边终点的入度减少1
			if(ru[v[k]]==0) q.push(v[k]);//当这个终点入度为0了,表明可以做这件事了,放入队列末尾,待做
			mx[v[k]]=max(mx[v[k]],mx[u[k]]+Time[v[k]]);//每件事昨晚的总共时间是做完它“最晚”前提条件+做它本身的时间,所以取max
		}
	}
	for(int i=1;i<=n;i++) ans=max(ans,mx[i]);//完成整个工程的时间就是做完最晚那个工程的时间
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)//读入数据
	{
		int a,b,c;
		scanf("%d %d %d",&a,&b,&c);
		Time[a]=b;//记录做每件事单独所用的时间
		while(c!=0)
		{
			ru[a]++;//统计各点的入度
			add(c,a);//这里使用邻接表存储图
			scanf("%d",&c);
		}
	}
	topology();//拓扑排序
	cout<<ans;
	return 0;
}

再回味一下,很简单对吧?

学习厌倦了?点我有更多精彩哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值