数据结构PTA习题:08-图9 关键活动 (30分)——关键路径

08-图9 关键活动 (30分)

假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。
任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。
请编写程序判定一个给定的工程项目的任务调度是否可行;如果该调度方案可行,则计算完成整个工程项目需要的最短时间,并输出所有的关键活动。

输入格式:
输入第1行给出两个正整数N(≤100)和M,其中N是任务交接点(即衔接相互依赖的两个子任务的节点,例如:若任务2要在任务1完成后才开始,则两任务之间必有一个交接点)的数量。交接点按1~N编号,M是子任务的数量,依次编号为1~M。随后M行,每行给出了3个正整数,分别是该任务开始和完成涉及的交接点编号以及该任务所需的时间,整数间用空格分隔。

输出格式:
如果任务调度不可行,则输出0;否则第1行输出完成整个工程项目需要的时间,第2行开始输出所有关键活动,每个关键活动占一行,按格式“V->W”输出,其中V和W为该任务开始和完成涉及的交接点编号。关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。

输入样例:

7 8
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2

输出样例:

17
1->2
2->4
4->6
6->7

关键路径问题。
主要思路是求出各顶点的最早完工时间和最晚完工时间,分别存入Earliest数组和Latest数组,据此判断是否是关键活动。

C语言实现:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define Infinity 10000
struct graph
{
	int Ne;
	int Nv;
	int * * M;
};
typedef struct graph * Graph;
struct que
{
	int front;
	int rear;
	int * s;
};
typedef struct que * Queue;
Queue CreateQ(int N);
void AddQ(Queue Q, int y);
int DeleteQ(Queue Q);
Graph CreateGraph(int N);
int * Visited;
int main()
{
	int N, M;
	scanf("%d %d", &N, &M);
	int i, j;
	Visited = (int *)malloc(N * sizeof(int));
	Graph G;
	G = CreateGraph(N);
	G->Ne = M;
	int v1, v2, w;
	for (i = 0; i < M; i++)//有向图
	{
		scanf("%d %d %d", &v1, &v2, &w);
		G->M[v1 - 1][v2 - 1] = w;
	}
	int * Earliest, *Latest, *Indegree;//Earliest数组记录最早完工时间,Latest数组记录最迟完工时间,Indegree数组记录顶点入度
	Earliest = (int *)malloc(N * sizeof(int));
	Latest = (int *)malloc(N * sizeof(int));
	Indegree = (int *)malloc(N * sizeof(int));
	for (i = 0; i < N; i++)//初始化
	{
		Earliest[i] = -1;
		Latest[i] = Infinity;
		Indegree[i] = 0;
		Visited[i] = 0;
	}
	for (i = 0; i < N; i++)//计算初始各顶点入度
	{
		for (j = 0; j < N; j++)
		{
			if (G->M[j][i] != -1)
			{
				Indegree[i]++;
			}
		}
	}
	//求各顶点的最早完工时间
	Queue Q;
	Q = CreateQ(N);
	for (i = 0; i < N; i++)//初始入度为0的顶点入队,最早完工时间=0,图中删除该顶点的出边
	{
		if (Indegree[i] == 0)
		{
			AddQ(Q, i);
			Earliest[i] = 0;
			Visited[i] = 1;
			for (j = 0; j < N; j++)
			{
				if (G->M[i][j] != -1)
				{
					Indegree[j]--;
				}
			}
		}
	}
	int V;
	while (Q->front != Q->rear)
	{
		V = DeleteQ(Q);
		for (i = 0; i < N; i++)
		{
			if (G->M[V][i] != -1)
			{
				if (Earliest[i] < Earliest[V] + G->M[V][i])
				{
					Earliest[i] = Earliest[V] + G->M[V][i];
				}
			}
		}
		for (i = 0; i < N; i++)
		{
			if (Indegree[i] == 0 && Visited[i] == 0)
			{
				AddQ(Q, i);
				Visited[i] = 1;
				for (j = 0; j < N; j++)
				{
					if (G->M[i][j] != -1 && Indegree[j] > 0)
					{
						Indegree[j]--;
					}
				}
			}
		}
	}
	//调试,输出各顶点的最早完工时间
	/*
	for (i = 0; i < N; i++)
	{
		printf("%5d", Earliest[i]);
	}
	printf("\n");
	*/
	//
	int flag = 0;
	for (i = 0; i < N; i++)
	{
		if (Earliest[i] != -1)
		{
			flag = 1;
		}
	}
	if (flag == 0) { printf("0"); }//调度任务不可行,图中存在环路的情况输出0
	else//调度任务可行
	{
		//求最早完工时间
		int max = Earliest[0];
		for (i = 1; i < N; i++)
		{
			if (Earliest[i] > max)
			{
				max = Earliest[i];
			}
		}
		printf("%d\n", max);//输出所有顶点最早完工时间中的最大值
		
		//求各顶点的最晚完工时间
		Q = CreateQ(N);
		for (i = 0; i < N; i++)
		{
			if (Earliest[i] == max)
			{
				AddQ(Q, i);
				Latest[i] = Earliest[i];
			}
		}
		while (Q->front != Q->rear)
		{
			V = DeleteQ(Q);
			for (i = 0; i < N; i++)
			{
				if (G->M[i][V] != -1)
				{
					if (Latest[i] > Latest[V] - G->M[i][V])
					{
						Latest[i] = Latest[V] - G->M[i][V];
					}
					AddQ(Q, i);
				}
			}
		}
		//调试,输出各顶点的最晚完工时间
		/*
		for (i = 0; i < N; i++)
		{
			printf("%5d", Latest[i]);
		}
		printf("\n");
		*/
		//
		//输出所有关键活动
		for (i = 0; i < N; i++)
		{
			for (j = N - 1; j >= 0; j--)
			{
				if (G->M[i][j] != -1 && Latest[j] == Earliest[i] + G->M[i][j])
				{
					printf("%d->%d\n", i + 1, j + 1);
				}
			}
		}
	}
	return 0;
}
Graph CreateGraph(int N)
{
	int i, j;
	Graph G;
	G = (Graph)malloc(sizeof(struct graph));
	G->Ne = 0;
	G->Nv = N;
	G->M = (int * *)malloc(N * sizeof(int *));
	for (i = 0; i < N; i++)
	{
		G->M[i] = (int *)malloc(N * sizeof(int));
	}
	for (i = 0; i < N; i++)
	{
		for (j = 0; j < N; j++)
		{
			G->M[i][j] = -1;
		}
	}
	return G;
}
Queue CreateQ(int N)
{
	Queue Q;
	Q = (Queue)malloc(sizeof(struct que));
	Q->front = Q->rear = -1;
	Q->s = (int *)malloc(N * sizeof(int));
	return Q;
}
void AddQ(Queue Q, int y)
{
	Q->rear++;
	Q->s[Q->rear] = y;
}
int DeleteQ(Queue Q)
{
	Q->front++;
	return Q->s[Q->front];
}
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值