用Topo序列求关键路径

//时间复杂度(n+e)  n:顶点数	e:边数
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>

using namespace std;

const int MAXN=10000;

struct Node{
	int x;
	int v;
};

vector<Node> Map[MAXN];
stack<int> num,T;						//T为拓扑序列顶点栈,num为零入度顶点栈
int cnt[MAXN],ve[MAXN],vm[MAXN];		//cnt表示各个顶点当前的入度,ve表示各个顶点事件的最早发生时间
										//vm表示各个顶点事件的最迟发生时间
int n,m;

bool Topo()
{
	int i,j,count=0;
	Node tmp;
	while(!T.empty())
		T.pop();
	while(!num.empty())
		num.pop();

	for(i=1;i<=n;i++)
	{
		if(cnt[i]==0)
			num.push(i);
	}

	while(!num.empty())
	{
		T.push(num.top());
		num.pop();
		++count;
		for(i=0;i<Map[T.top()].size();i++)
		{
			tmp=Map[T.top()][i];
			cnt[tmp.x]--;
			if(cnt[tmp.x]==0)
				num.push(tmp.x);
			if(ve[tmp.x]<tmp.v+ve[T.top()])
				ve[tmp.x]=tmp.v+ve[T.top()];
		}
	}
	if(count<n)
		return false;
	return true;
}

bool CriticalPath()
{
	int i,j;
	Node tmp;
	if(Topo()==false)
		return false;
	for(i=1;i<=n;i++)					//初始化顶点事件的最迟发生时间
		vm[i]=ve[n];
	while(!T.empty())					//按拓扑逆序求各顶点的vm值
	{
		j=T.top();
		T.pop();
		for(i=0;i<Map[j].size();i++)
		{
			tmp=Map[j][i];
			if(vm[j]>vm[tmp.x]-tmp.v)
				vm[j]=vm[tmp.x]-tmp.v;
		}
	}

	printf("\n");
	for(i=1;i<=n;i++)									//求关键活动
	{
		for(j=0;j<Map[i].size();j++)
		{
			tmp=Map[i][j];
			if(ve[i]==vm[tmp.x]-tmp.v)
				printf("%d %d %d\n",i,tmp.x,tmp.v);		//输出关键路径
		}
	}

}

int main()
{
	int i,j,a;
	Node tmp;
	while(scanf("%d%d",&n,&m)==2)
	{
		for(i=1;i<=n;i++)
		{
			Map[i].clear();
			cnt[i]=0;
			ve[i]=0;
		}

		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d",&a,&tmp.x,&tmp.v);
			Map[a].push_back(tmp);
			cnt[tmp.x]++;				//求各定点的入度
		}

		if(CriticalPath()==false)
			printf("ERROR\n");
		else
		{
			printf("\n");
			for(i=1;i<=n;i++)
			{
				printf("%d %d %d\n",i,ve[i],vm[i]);
			}
		}
	}

	return 0;
}

<think>嗯,用户想了解拓扑排序关键路径的概念及实现方法。首先,我得确保自己正确理解这两个概念。拓扑排序是处理有向无环图(DAG)中节点的一种线性序列,满足所有有向边从前往后的方向。关键路径则是在项目管理中用来确定项目最短完成时间的路径,这可能涉及到AOE网(Activity On Edge Network)。接下来,我需要分步骤解释这两个概念,并给出实现方法。 首先,拓扑排序的步骤。我应该先解释什么是DAG,然后说明拓扑排序的应用场景,比如任务调度、依赖解析。然后,算法步骤,比如计算入度,使用队列,逐步处理入度为0的节点,并更新相关节点的入度。这里可能需要提到Kahn算法。代码实现方面,可以用Python,用邻接表表示图,队列可以用deque或者普通列表。然后,给出一个示例代码,比如处理一个简单的图结构,并输出拓扑序列。 然后,关键路径关键路径涉及到最早开始时间和最晚开始时间,以及活动的差额是否为0。需要解释AOE网中的事件(顶点)和活动(边),以及如何通过拓扑排序和逆拓扑排序计算最早和最晚时间。算法步骤可能需要先进行拓扑排序,然后计算每个事件的最早开始时间,接着进行逆拓扑排序计算最晚开始时间,最后确定关键活动。代码实现方面,可能需要先获取拓扑序列,然后正反两次遍历来计算时间,最后比较时间差找出关键路径。这里要注意,如果图中有多个起点或终点,可能需要特殊处理。 在实现过程中,需要注意异常情况的处理,比如图中存在环,这时候拓扑排序无法完成,需要检测这种情况。此外,关键路径计算必须基于有效的拓扑排序结果,否则结果不准确。代码示例部分,可能需要构造一个AOE网的例子,比如包含多个事件和活动,边带有权重(时间),然后逐步计算各个事件的时间参数,最后输出关键路径。 用户可能还会关心这两个算法的应用场景,比如拓扑排序用于编译顺序、课程安排,关键路径用于项目管理和进度控制。此外,可能需要提到关键路径法(CPM)在项目管理中的重要性,以及如何通过调整关键活动来缩短项目周期。 在生成相关问题的时候,应该围绕这两个主题展开,比如拓扑排序的算法优化,如何检测环,关键路径的实际应用案例,以及两者的复杂度分析。可能还要考虑用户对算法实现的具体疑问,比如如何处理多个入度为0的节点,或者关键路径存在多条的情况。 现在需要检查是否有遗漏的部分。比如,拓扑排序的Kahn算法和DFS方法,但用户可能只需要其中一种实现。关键路径中的逆拓扑排序可能需要使用拓扑序列的反向遍历。另外,代码示例要确保正确,比如在计算最晚时间时,初始值设置为最大值还是最早时间中的最大值。这些细节需要准确处理,避免误导用户。 最后,确保回答符合用户的要,结构清晰,分点说明,代码正确,并且引用了相关的概念,比如AOE网和关键路径法。同时,在相关问题部分,覆盖可能的扩展问题,帮助用户深入理解。</think>### 拓扑排序 拓扑排序用于对有向无环图(DAG)进行线性排序,使得任意边的终点总在起点之后。其算法步骤如下: 1. 统计所有顶点的入度 2. 将入度为0的顶点加入队列 3. 依次取出顶点并更新相邻顶点的入度 4. 重复直到队列为空 时间复杂度为$O(V+E)$,空间复杂度$O(V)$[^1] ```python from collections import deque def topological_sort(graph): in_degree = {u:0 for u in graph} for u in graph: for v in graph[u]: in_degree[v] += 1 queue = deque([u for u in in_degree if in_degree[u] == 0]) result = [] while queue: u = queue.popleft() result.append(u) for v in graph[u]: in_degree[v] -= 1 if in_degree[v] == 0: queue.append(v) return result if len(result) == len(graph) else [] # 检测环 ``` ### 关键路径 关键路径是AOE网(Activity on Edge Network)中决定项目最短工期的路径,具有以下特征: - 路径上所有活动的总时差为0 - 由关键活动构成的最长路径 实现步骤: 1. 计算各顶点最早开始时间$ve$ 2. 计算各顶点最晚开始时间$vl$ 3. 计算各边活动的最早/最晚开始时间 4. 确定关键活动 $$ve[j] = \max\{ve[i] + weight(i,j)\}$$ $$vl[i] = \min\{vl[j] - weight(i,j)\}$$ ```python def critical_path(vertices, edges): # 拓扑排序 topo_order = topological_sort(vertices) if not topo_order: return [] # 初始化时间数组 ve = {u:0 for u in vertices} vl = {u:ve[topo_order[-1]] for u in vertices} # 计算最早时间 for u in topo_order: for v, w in edges.get(u, []): if ve[v] < ve[u] + w: ve[v] = ve[u] + w # 计算最晚时间 for u in reversed(topo_order): for v, w in edges.get(u, []): if vl[u] > vl[v] - w: vl[u] = vl[v] - w # 确定关键路径 critical_edges = [] for u in edges: for v, w in edges[u]: e = ve[u] l = vl[v] - w if e == l: critical_edges.append((u, v)) return critical_edges ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值