[PTA] 7-11 关键活动 最短答案!

题目详情
题目分析:

  • 全网最短代码,没有之一!
  • 这一题要注意两点
    • 松节点,与该节点相连的所有路径都不是关键路径。规则如下:
      1.如果该结点发出的所有边,距离下个结点的开始时间都有富余,那么该节点是松节点
      2.若一个节点发出的所有边都连到了松节点上,那么该点也是松节点
      3.如果存在多个终点 该节点为终点,且结束时间比最晚的终点要早,那么该终点为松节点
    • 求逆拓扑序列只需将拓扑序列倒排一下即可

特殊的输入样例

11 14
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2
8 3 7
9 3 7
9 10 6
4 10 2
10 6 5
6 11 4

输出样例

21
3->4
4->10
6->11
8->3
9->3
10->6

注意这里面没有6->7
图中终点最早发生时间为21,而7这个终点最早发生时间为19,所以7这个终点是松节点,不能作为关键路径输出

最短答案!还有什么简便的好方法欢迎讨论。

#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef int Node_Number;//Node_Number节点编号
int queue[111], now, max_time, front, rear, counts, StartNode, EndNode, NodeNum, EdgeNum, cost;

struct Edge
{
	int Target_point;//边的目标点
	int timecost;//活动花费的时间
	Edge(int d, int p) : Target_point(d), timecost(p) {}//构造函数
};

struct Node
{
	int earliest;
	int latest = 0x3f3f3f3;//最晚完成时间设为无穷大
	int indegree;//入度
	int outdegree;//出度

	vector<Edge> inedge;//入边
	vector<Edge> outedge;//出边
};

void CalculateETime(map<Node_Number, Node> &Graph);//计算事件最早完成时间
void CalculateLTime(map<Node_Number, Node> &Graph);//计算使事件最晚完成时间
void PrintKeyRoads(map<Node_Number, Node> &Graph);//输出关键路径
int main()
{
	map<Node_Number, Node> Graph;//声明图
	cin >> NodeNum >> EdgeNum;
	for (int i = 1; i <= EdgeNum; ++i)
	{
		cin >> StartNode >> EndNode >> cost;
		Graph[EndNode].inedge.push_back(Edge(StartNode, cost));
		Graph[StartNode].outedge.push_back(Edge(EndNode, cost));//建边
		Graph[EndNode].indegree++;
		Graph[StartNode].outdegree++;
	}
	CalculateETime(Graph);
	CalculateLTime(Graph);
	PrintKeyRoads(Graph);
}
void CalculateETime(map<Node_Number, Node> &Graph)
{
	for (map<Node_Number, Node>::iterator iter = Graph.begin(); iter != Graph.end(); ++iter)
		if ((*iter).second.indegree == 0)//可能有多个起点,将所有入度为零的点加入队列
		{
			queue[++rear] = (*iter).first;
			Graph[(*iter).first].earliest = 0;//初始化事件最早完成时间
		}
	while (front != rear)
	{
		counts++;//计数图中能够拓扑排序的节点个数
		now = queue[++front];//出队
		for (vector<Edge>::iterator iter = Graph[now].outedge.begin(); iter != Graph[now].outedge.end(); ++iter)
		{
			Graph[(*iter).Target_point].earliest = max(Graph[(*iter).Target_point].earliest, Graph[now].earliest + (*iter).timecost);//spfa算法计算最长路径,最长路径就是事件最早完成时间
			if (--Graph[(*iter).Target_point].indegree == 0)//遍历到一个点就删掉一条到目标点的出边,直到目标点为入度零,将目标点加入队列
			{
				queue[++rear] = (*iter).Target_point;
				max_time = max(max_time, Graph[(*iter).Target_point].earliest);//获得最长时间,即最早完成时间
			}
		}
	}
	if (counts != Graph.size())//图中能够拓扑排序的节点个数小于图中节点的总个数,说明存在环结构,不能拓扑排序
		cout << 0, exit(0);//结束程序
	else
		cout << max_time << endl;//否则,输出最长时间,即最早完成时间
}
void CalculateLTime(map<Node_Number, Node> &Graph)
{
	for (int i = Graph.size(); i > 0;)
	{
		now = queue[i--];
		if (Graph[now].outdegree == 0 && Graph[now].earliest == max_time)//可能存在多个终点,获得出度为零的节点,且最早完成时间要等于整个工程的最早完成时间,才初始化。排除松节点,这一步很关键!!
			Graph[now].latest = Graph[now].earliest;
		for (vector<Edge>::iterator iter = Graph[now].inedge.begin(); iter != Graph[now].inedge.end(); ++iter)
			Graph[(*iter).Target_point].latest = min(Graph[now].latest - (*iter).timecost, Graph[(*iter).Target_point].latest);
	}
}
void PrintKeyRoads(map<Node_Number, Node> &Graph)
{
	for (int now = 1; now <= Graph.size(); now++)
		for (vector<Edge>::reverse_iterator r_iter = Graph[now].outedge.rbegin(); r_iter != Graph[now].outedge.rend(); ++r_iter)//题目要求起点编号相同时,与输入时任务的顺序相反,所以反过来遍历
			if (Graph[(*r_iter).Target_point].latest - (*r_iter).timecost == Graph[now].earliest)
				cout << now << "->" << (*r_iter).Target_point << endl;
}

References:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值