求关键路径

虽然可以实现基本的求图的关键路径的功能,但是存在瑕疵,必须保证图的下标最小的节点是整个图的源点,下标最大的节点是整个图的汇点 (工程编码思维)

1.求图的拓扑排序

#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <stack>

#define FAST ios::sync_with_stdio(false)
#define LL long long

using namespace std;
const int N = 1e5 + 10;

int Head[N],tot,st[N],n,m,indegree[N];
stack<int> s; // 用来存当前入度为0的顶点的序号 

struct E{
	int to,next;
}Edge[N];

void add(int a,int b) // 读入边的操作 
{
	Edge[tot].to = b;
	Edge[tot].next = Head[a];
	
	Head[a] = tot ++;	
} 

int find_du(void) // 遍历确定图中每一个点的入度 
{
	for(int i = 1;i <= n;i++)
	{
		for(int j = Head[i] ; j != -1 ; j = Edge[j].next)
		{
			indegree[Edge[j].to] ++;
		}
	}
}

vector<int>  Topsort()
{
	for(int i = 1;i <= n;i++)
	{
		if(indegree[i] == 0)
		{
			s.push(i);
		}
	} // 先存入初始的时候度就为 0 的点
	
	int count = 0; // 用来记录当前确定了拓扑序列的点的数目
	vector<int> ans;
	
	while(!s.empty()) // 每次取出一个入度为0的点,同时删去与这个点有关的所有边,创造新的入度为0的边 
	{
		int temp = s.top(); 
		count ++ ;
		s.pop();
		
		ans.push_back(temp);
		
		for(int i = Head[temp] ; i != -1 ; i = Edge[i].next)
		{
			indegree[Edge[i].to] -- ; // 删除以这个点为起点的终点的顶点入度 
			if(indegree[Edge[i].to] == 0) s.push(Edge[i].to); // 检查是否有新的入度为0的点产生 
		}
	}
	
	if(count == n) // 这个图中的所有的顶点都可以确定其的拓扑序列位置 
	{
		cout << "Yes" << endl;
		return ans;
	}
	else
	{
		cout << "No" << endl;
		ans.clear();
		return ans;
	}
} 

int main(void)
{
	FAST;
	memset(Head,-1,sizeof Head);
	
	cin >> n >> m;
	
	while(m --)
	{
		int a,b;
		cin >> a >> b;
		
		add(a,b); // 有向无环图	
	}
	
	find_du();
	
	vector<int> Top = Topsort();
	
	for(auto it : Top) cout << it << " ";
	
	return 0;
}

  1. 上述+ 求每个节点的最早发生时间 + 求图的逆拓扑排序
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <stack>

#define FAST ios::sync_with_stdio(false)
#define LL long long

using namespace std;
const int N = 1e5 + 10;

int Head[N],tot,st[N],n,m,indegree[N],ve[N]; // ve为每个事件的最早发生时间 (即到根节点的最远距离) 
stack<int> s; // 用来存当前入度为0的顶点的序号 

struct E{
	int to,next,w;
}Edge[N];

void add(int a,int b,int w) // 读入边的操作 
{
	Edge[tot].to = b;
	Edge[tot].next = Head[a];
	Edge[tot].w = w;
	Head[a] = tot ++;	
} 

int find_du(void) // 遍历确定图中每一个点的入度 
{
	for(int i = 1;i <= n;i++)
	{
		for(int j = Head[i] ; j != -1 ; j = Edge[j].next)
		{
			indegree[Edge[j].to] ++;
		}
	}
}

stack<int>  Topsort() // 返回逆序的拓扑排序序列 
{
	for(int i = 1;i <= n;i++)
	{
		if(indegree[i] == 0)
		{
			s.push(i);
		}
	} // 先存入初始的时候度就为 0 的点
	
	int count = 0; // 用来记录当前确定了拓扑序列的点的数目
	stack<int> ans;
	memset(ve,0,sizeof ve); // 初始化ve数组 
	
	while(!s.empty()) // 每次取出一个入度为0的点,同时删去与这个点有关的所有边,创造新的入度为0的边 
	{
		int temp = s.top(); 
		count ++ ;
		s.pop();
		
		ans.push(temp);
		
		for(int i = Head[temp] ; i != -1 ; i = Edge[i].next)
		{
			indegree[Edge[i].to] -- ; // 删除以这个点为起点的终点的顶点入度 
			if(indegree[Edge[i].to] == 0) s.push(Edge[i].to); // 检查是否有新的入度为0的点产生 
			
			// 当前节点到根节点的距离 + 当前节点到其临边的边的权值  > 其临边所指向节点到根节的距离 就 直接更新这个临边节点到根节点的距离 
			if(ve[temp] + Edge[i].w > ve[Edge[i].to]) ve[Edge[i].to] = ve[temp] + Edge[i].w;
		}
	}
	
	if(count == n) // 这个图中的所有的顶点都可以确定其的拓扑序列位置 
	{
		cout << "Yes" << endl;
		return ans;
	}
	else
	{
		cout << "No" << endl; // 不存在拓扑排序的话就直接结束程序的运行 
		exit(0);
	}
} 

int main(void)
{
	FAST;
	memset(Head,-1,sizeof Head);
	
	cin >> n >> m;
	
	while(m --)
	{
		int a,b,w;
		cin >> a >> b >> w;
		
		add(a,b,w); // 有向无环图	
	}
	
	find_du();
	
	stack<int> Verse_Top = Topsort();
	
	return 0;
}

  1. 上述 + 求每个节点的最晚发生时间 + 求图的关键路径
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <stack>

#define FAST ios::sync_with_stdio(false)
#define LL long long

using namespace std;
const int N = 1e5 + 10;

int Head[N],tot,st[N],n,m,indegree[N],ve[N],vl[N]; // ve为每个事件的最早发生时间 (即到根节点的最远距离) vl[N]为每个事件的最迟开始时间(逆拓扑序列依次减去边权值即可) 
stack<int> s; // 用来存当前入度为0的顶点的序号 

struct E{
	int to,next,w;
}Edge[N];

void add(int a,int b,int w) // 读入边的操作 
{
	Edge[tot].to = b;
	Edge[tot].next = Head[a];
	Edge[tot].w = w;
	Head[a] = tot ++;	
} 

int find_du(void) // 遍历确定图中每一个点的入度 
{
	for(int i = 1;i <= n;i++)
	{
		for(int j = Head[i] ; j != -1 ; j = Edge[j].next)
		{
			indegree[Edge[j].to] ++;
		}
	}
}

stack<int>  Topsort() // 返回逆序的拓扑排序序列 
{
	for(int i = 1;i <= n;i++)
	{
		if(indegree[i] == 0)
		{
			s.push(i);
		}
	} // 先存入初始的时候度就为 0 的点
	
	int count = 0; // 用来记录当前确定了拓扑序列的点的数目
	stack<int> ans;
	memset(ve,0,sizeof ve); // 初始化ve数组 
	
	while(!s.empty()) // 每次取出一个入度为0的点,同时删去与这个点有关的所有边,创造新的入度为0的边 
	{
		int temp = s.top(); 
		count ++ ;
		s.pop();
		
		ans.push(temp);
		
		for(int i = Head[temp] ; i != -1 ; i = Edge[i].next)
		{
			indegree[Edge[i].to] -- ; // 删除以这个点为起点的终点的顶点入度 
			if(indegree[Edge[i].to] == 0) s.push(Edge[i].to); // 检查是否有新的入度为0的点产生 
			
			// 当前节点到根节点的距离 + 当前节点到其临边的边的权值  > 其临边所指向节点到根节的距离 就 直接更新这个临边节点到根节点的距离 
			if(ve[temp] + Edge[i].w > ve[Edge[i].to]) ve[Edge[i].to] = ve[temp] + Edge[i].w;
		}
	}
	
	if(count == n) // 这个图中的所有的顶点都可以确定其的拓扑序列位置 
	{
		cout << "Yes" << endl;
		return ans;
	}
	else
	{
		cout << "No" << endl; // 不存在拓扑排序的话就直接结束程序的运行 
		exit(0);
	}
} 

void Find_CriticalPath(stack<int> T) // 寻找关键路径的函数 
{
	vector<int> path;
	
	if(T.empty())
	{
		cout << "Stakc T is empty " << endl;
		return ; 
	}
	
	for(int i = 1;i <= n;i++) // 初始化所有节点的最迟发生时间为汇点的最早发生时间 再来进行依次的减去边权的操作 
	{
		vl[i] = ve[n]; // 人为规定下标最大的节点是图中的汇点 (没错 工程编程思维,最后一个节点就是整个图的汇点) 
	}
	
	while(!T.empty()) // 去处理拓扑排序 (间接地去处理每一个节点) 
	{
		int temp = T.top();
		T.pop();
		
		for(int i = Head[temp] ; i != -1 ; i = Edge[i].next)
		{
			// 如果一个节点是多条边的起点 在反过来找这个节点的最晚发生时间的时候 是找边终点减去边权值的最小值作为该节点的最晚发生时间 
			if(vl[Edge[i].to] - Edge[i].w < vl[temp]) vl[temp] = vl[Edge[i].to] - Edge[i].w;
		}
	}
	
	for(int i = 1;i <= n;i++) // 依次去遍历每一个节点 又从每一个点出发遍历这个点的所有临点 
	{
		for(int j = Head[i] ; j != -1 ; j = Edge[j].next) // 遍历临点 
		{
			int ei = ve[i]; // 当前起点的最早发生时间 
			int li = vl[Edge[j].to] - Edge[j].w; // 临点的最迟发生时间 - 临点到起点的边的权值 (倒推起点的最迟发生时间的前一步) 
			
			if(ei == li) // 当前节点的最早发生时间 和 最迟发生时间相等 这个节点以及使得其满足条件的这条边的终点 构成的边成为关键路径
			// Tips : 每条关键路径的成立依托两个点 
			{
				cout << i << " " << Edge[j].to << endl;
			}
		}
	} 
}

int main(void)
{
	FAST;
	memset(Head,-1,sizeof Head);
	
	cin >> n >> m;
	
	while(m --)
	{
		int a,b,w;
		cin >> a >> b >> w;
		
		add(a,b,w); // 有向无环图	
	}
	
	find_du();
	
	stack<int> Verse_Top = Topsort();
	
	Find_CriticalPath(Verse_Top);
	
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值