How Long Does It Take - 拓扑排序

前言

思路可以看看我的,代码就别抄我的了,还有最后一个结点没通过,不想找哪里出错了....

题目

Given the relations of all the activities of a project, you are supposed to find the earliest completion time of the project.

输入格式

Each input file contains one test case. Each case starts with a line containing two positive integers N (≤100), the number of activity check points (hence it is assumed that the check points are numbered from 0 to N−1), and M, the number of activities. Then M lines follow, each gives the description of an activity. For the i-th activity, three non-negative numbers are given: S[i], E[i], and L[i], where S[i] is the index of the starting check point, E[i] of the ending check point, and L[i] the lasting time of the activity. The numbers in a line are separated by a space.

输出格式

For each test case, if the scheduling is possible, print in a line its earliest completion time; or simply output "Impossible".

样例

输入样例1

9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4

输出样例1

18

输入样例2

4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5

输出样例2

Impossible

思路

用拓扑排序来解决一个简化版的关键路径问题,在这个问题中,只要求输出最后的工期耗时即可,并不要求输出路径点。
拓扑排序的算法就是利用一个栈作为辅助,先将作为入度为0的结点入栈,然后出栈一个元素,表示从图中删除这个结点,并更新所有这个结点指向的结点的相关信息(入度,最大消耗时间),然后再选择入度为0的元素入栈,再出栈一个元素并重复这个过程直至栈空,在出栈过程中用一个count变量统计出栈元素数目,如果最后该数等于总的结点数,那就说明算法成功,反之则不成功,要么有环,要么出现了不连通的情况等。

实现

完整代码

看看思路就行,有一个我不知道的bug潜伏着....如果能帮我找出来,再三感激...

#include<iostream>
using namespace std;

const int MaxSize = 101;

typedef struct ArcNode{				
	int index;									
	int data;									
	struct ArcNode *next;				
}ArcNode;

typedef struct VNode {					
	int etime;									
	int ind;										
	ArcNode* firstArc;					
}VNode;

typedef struct {								
	VNode adjlist[MaxSize];			
	int n, e;										
}AGraph;

AGraph G;

void myInput(AGraph &g);
ArcNode* myInsert(int v, int values);
void myTopSort(AGraph g);

int main() {
	int N, M;
	cin >> N >> M;
	G.n = N;
	G.e = M;
	myInput(G);
	myTopSort(G);
	return 0;
}

void myInput(AGraph &g) {		//图的输入和初始化
	for (int i = 0; i < g.n; i++) {		//将一些元素重置0
		g.adjlist[i].etime = 0;
		g.adjlist[i].ind = 0;
		g.adjlist[i].firstArc = NULL;
	}
	for (int i = 0; i < g.e; i++) {			//使用头插法插入新的相邻结点
		int v1, v2, value;
		cin >> v1 >> v2 >> value;
		g.adjlist[v2].ind++;					//更新入度
		ArcNode* p = g.adjlist[v1].firstArc;
		g.adjlist[v1].firstArc = myInsert(v2, value);
		g.adjlist[v1].firstArc->next = p;
	}
}

ArcNode* myInsert(int v, int values) {	//新建一个结点
	ArcNode* p = new ArcNode;
	p->data = values;
	p->index = v;
	p->next = NULL;
	return p;
}

void myTopSort(AGraph g) {
	int stack[MaxSize];
	int top = -1;
	int count = 0;
	for (int i = 0; i < g.n; i++)			
		if (g.adjlist[i].ind == 0)
			stack[++top] = i;
	while (top != -1) {					
		int i = stack[top--];				
		ArcNode* p = g.adjlist[i].firstArc;		
		count++;							
		//cout << i << " ";
		while (p != NULL){				
			int j = p->index;
			g.adjlist[j].ind--;				
			if(g.adjlist[j].etime < g.adjlist[i].etime + p->data)		
				g.adjlist[j].etime = g.adjlist[i].etime + p->data;		
			if (g.adjlist[j].ind == 0)		
				stack[++top] = j;
			p = p->next;
		}
	}
	if (count == g.n)
		cout << g.adjlist[stack[top + 1]].etime;			
	else
		cout << "Impossible";
}

存储

第一次用邻接表做题,感觉挺不错的,与邻接矩阵相比有利有害。
利:适合用来存储稀疏图不适合用来存储稠密图;
害:输入的时候不是很方便,还要新建结点ba'la'ba;不容易快速找到两个结点是否有边相连

typedef struct ArcNode{				//边结构
	int index;									//这个边另外一个点的index
	int data;									//边的信息,在本题中是权值
	struct ArcNode *next;				//指向下一条边
}ArcNode;

typedef struct VNode {					//结点结构
	int etime;									//该结点的最早完成时间,在算法中实时更新
	int ind;										//点的入度
	ArcNode* firstArc;					//指向第一条边
}VNode;

typedef struct {								//图结构
	VNode adjlist[MaxSize];			//结点数组
	int n, e;										//结点数、边数等关键信息
}AGraph;

拓扑排序的核心算法

详细说明见备注

void myTopSort(AGraph g) {
	int stack[MaxSize];
	int top = -1;
	int count = 0;
	for (int i = 0; i < g.n; i++)			//所有入队为0的结点入栈
		if (g.adjlist[i].ind == 0)
			stack[++top] = i;
	while (top != -1) {					//栈空时结束算法,或者是根本没有入栈的结点
		int i = stack[top--];				//出栈一个
		ArcNode* p = g.adjlist[i].firstArc;		//开始遍历该结点的所有边
		count++;								//出栈一个元素计数一个
		//cout << i << " ";
		while (p != NULL){				//一个结点出栈只会影响跟他相连的结点的出度
			int j = p->index;
			g.adjlist[j].ind--;				//出栈的顶点指向的所有顶点的入度减一
			if(g.adjlist[j].etime < g.adjlist[i].etime + p->data)		//更新最早完成时间:上一个结点的最早完成时间+该边上的活动需要的时间
				g.adjlist[j].etime = g.adjlist[i].etime + p->data;		
			if (g.adjlist[j].ind == 0)		//入度为0则入栈
				stack[++top] = j;
			p = p->next;
		}
	}
	if (count == g.n)
		cout << g.adjlist[stack[top + 1]].etime;			//输出栈内的最后一个元素即为最后一个完成的结点的最早完成时间就是整个工程的最早完成时间
	else
		cout << "Impossible";
}
posted @ 2020-04-29 15:28  游芒。  阅读(210)  评论(0)    收藏  举报