4图-7关键路径(含拓扑排序)

#include 
using namespace std;
const int maxn = 1000;
struct Node{int v, w;}node;     //终点编号,边权(活动时长)
vector G[maxn];           //邻接表存边,即存活动
int n,inDegree[maxn] = { 0 };   //点数,存每个点的入度
stack topOrder;            //拓扑排序栈
int ve[maxn],vl[maxn];          //对结点来说,即事件是时间点,是最早开始时间,最迟结束时间(可以理解为休息时间区间)
                                //对这个点射出的边权来说,即活动的最早开始时间和最晚开始时间
vector pre[maxn];          //存每个点的前驱
bool topologicalSort(){         //拓扑排序,用G与inDegree去更新topOrder和ve
	queue q;               //开一个队列
	int c = 0;                  //当前队列中的点数
	for (int i = 0; i < n; i++) //逐个点遍历
		if (inDegree[i] == 0)q.push(i);//入度为零的点入队
	while (!q.empty()){         //队列非空
		int u=q.front();q.pop();//读出队
		c++;                    //入队点数加1
		topOrder.push(u);       //此点可以入排序栈
		for (int i = 0; i < G[u].size(); i++){  //射出的边逐条遍历
			int v = G[u][i].v, w = G[u][i].w;   //读出终点与边权
			inDegree[v]--;                      //此点入度减一
			if(inDegree[v] == 0)q.push(v);      //入度为0就压入这个点
			if(ve[u]+w>ve[v])ve[v]=ve[u]+w;     //距离松驰
		}
	}
	return c==n?true:false;     //排序排了C个点,不能成功把N个点排完就FALSE
}
vector path;
void DFS(int s,int e){          //传入起点与当前点,利用pre去构造path去输出
	if(s==e){                   //到达起点就进行以下输出并返回
		path.push_back(s);      //路径数组压入终点
		for (int i=path.size()-1;i>=0;i--){ //逆向输出
			printf("%d",path[i]);   //输出当前点编号
			if(i>0)printf("->");    //然后是箭头
			else printf("\n");      //故意留个0来输出个换行而已
		}
		path.pop_back();        //删掉最后一个元素
		return;
	}//未到终点就下面的代码
	path.push_back(e);          //把当前点压入路径数组(是反向存的)
	for(int i=0;i%d\n", u + 1, v + 1);   //输出时加1以还原输入时做出的改变
				pre[v + 1].push_back(u + 1);        //射向点v(+1)的前驱就是u(+1),此句是为了下面输出路径用
			}
		}
	}
	printf("所有的关键路径如下所示:\n");   //提示语,下面开始输出关键路径(再深搜一次)
	DFS(1, n);                              //深搜得到路径
	return ve[n - 1];                       //n-1号点最早开始时间是完成时间,最后一个点是最后一个活动结束这个事件的时间点
}
int main(){
	//freopen("i.txt", "r", stdin);     //键盘输入流重定向为i.txt文件输入流
	int m,a,b,w;                        //边数,临时变量A,B,W
	scanf("%d %d",&n,&m);               //n为点数,m为边数
	for (int i = 0; i < m; i++){        //逐条边输入
		scanf("%d%d%d", &a, &b, &w);    //a、b为事件,w为ab这条边所表示的活动耗费的时间
		node.v = b-1; node.w = w;       //结点存值方便下一句压入邻接表
		G[a-1].push_back(node);         //邻接表存图,为了方便a、b都减1
	}
	printf("关键路径长度为:%d\n",criticalPath());  //输出关键路径长度
	return 0;
}
/*
输入为(即i.txt内容):
9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
*/
/*
简单来说有四步
第一步拓扑排序及求ve:确保无环,同时理新好topOrder栈及ve,即每个结点的最早开始休息时间,或者说是此结点射出的每个边权活动最早开始时间(若只求关键路径长度已可输出)
第二步更新vl,利用已更新好的汇点最早开始休息时间及topOrder栈倒序更新每一个点的vl,即每个结点的最晚结束休息时间,或者说是此结点射出的每个边权活动最晚开始时间
第三步求关键活动:边权活动最早开始时间e就是边起点ve,最迟结束时间l是边终点vl-边权w,若e==l就是关键活动,并记录pre即每个点的前驱
第四步求关键路径:利用pre进行深搜,开path记录当前深搜的序列,当搜到
*/


你可能感兴趣的:(考研计机)