拓补排序

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

void topo()
{
	for(int i=1;i<=n;i++)
	{
        //入度为0,则加入 答案 和 入度为0的点中
		if(IN[i]==0)
		{
			zero_in.push_back(i);
			ans.push_back(i);
		}
	}
	while(!zero_in.empty())
	{
		int zp=zero_in.front();
		zero_in.erase(zero_in.begin());
        //取出 入度为0的点,并删除
		for(int i=head[zp];i;i=edge[i].nxt)
		{
			int to=edge[i].to;
            //删除该点后,与之相连的子节点的入度就少了1
			IN[to]--;
            //删去该点后,子节点可能出现新的入度为0的点
			if(IN[to]==0)
			{
				zero_in.push_back(to);
				ans.push_back(to);
			}
		}
	}
}

int main() 
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		while(true)
		{
			cin>>to;
			if(to==0) break;
			add(i,to);
            //记录每个点的入度
			IN[to]++;
		}
	}
	topo();
	return 0;
}	

运用:

首先,拓扑排序是针对有向无环图(DAG)的,用来对顶点进行线性排序,使得所有的有向边都从排在前面的顶点指向后面的顶点。

通俗地讲,就是完成 C 之前要先完成 B ,完成 B 之前要完成 A ......
拓补排序就是把这个顺序从先到后排列出来。

具体类型大致有以下几类

  1. 任务调度与依赖管理
    问题类型:确定任务的执行顺序,满足所有前置依赖。
    例子:
    课程安排:如“课程表”问题(LeetCode 207、210),给定课程间的先修关系,判断能否完成所有课程,并输出合理的学习顺序。
    项目管理:任务A必须在任务B之前完成,求可行的执行顺序。
    编译依赖:确定代码模块的编译顺序(如Makefile)。

  2. 检测有向图中的环
    问题类型:判断图中是否存在循环依赖。
    例子:
    如果无法生成拓扑排序(排序结果包含的节点数 < 总节点数),则图中存在环。
    应用场景:死锁检测(如资源循环请求)、依赖冲突分析。

  3. 计算DAG的最长路径
    问题类型:在DAG中,结合拓扑排序和动态规划计算最长路径。
    例子:
    矩阵中的最长递增路径(LeetCode 329)。
    关键路径问题:项目管理中估算最短完成时间。
    旅行计划

  4. 实际场景中的顺序规划
    问题类型:解决需要严格先后顺序的问题。
    例子:
    工序安排:工厂流水线中工序的先后顺序。
    菜谱步骤:某些烹饪步骤必须在其他步骤之前完成(如先煮水再泡茶)。

posted @ 2025-03-14 11:46  石磨豆浆  阅读(15)  评论(0)    收藏  举报