一、题目
一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。
输入格式:
首先第一行给出两个正整数:项目里程碑的数量 N(≤100)和任务总数 M。这里的里程碑从 0 到 N−1 编号。随后 M 行,每行给出一项任务的描述,格式为“任务起始里程碑 任务结束里程碑 工作时长”,三个数字均为非负整数,以空格分隔。
输出格式:
如果整个项目的安排是合理可行的,在一行中输出最早完工时间;否则输出"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
二、思路
有向图在工业中的应用。考查AOV网络、拓扑排序以及关键路径问题。
项目安排是否合理可行:有向图是否存在回路(不存在则满足拓扑排序);
最早完工时间:即关键路径长度,从源点到汇点的最长的通路。
三、代码
数据结构使用的是图,存储方式为邻接矩阵(邻接表会更好)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
using namespace std;
#define MaxVertexNum 200 /* 最大顶点数设为200 */
typedef int Vertex; /* 用顶点下标表示顶点,为整型 */
typedef int WeightType; /* 边的权值设为整型 */
/* 边定义 */
typedef struct ENode* PtrToENode;
struct ENode {
Vertex v1, v2; /* 有向边<V1, V2> */
WeightType weight; /* 权重 */
};
typedef PtrToENode Edge;
/* 图结点的定义 */
typedef struct GNode* PtrToGNode;
struct GNode {
int Nv; /* 顶点数 */
int Ne; /* 边数 */
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */
/*函数声明*/
MGraph CreateGraph(int VertexNum);//初始化一个有VertexNum个顶点但没有边的图
void InsertEdge(MGraph Graph, Edge E);// 插入边 <V1, V2>
MGraph BuildGraph();//图的构建
int max(int x, int y);
bool TopSort(MGraph Graph);//拓扑排序
/*主程序*/
int main() {
MGraph graph = BuildGraph();
TopSort(graph);
}
MGraph CreateGraph(int VertexNum)
{
Vertex v, u;
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图 */
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化邻接矩阵 */
/* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
for (v = 0; v < Graph->Nv; v++) {
for (u = 0; u < Graph->Nv; u++) {
Graph->G[v][u] = 0;
}
}
return Graph;
}
void InsertEdge(MGraph Graph, Edge E)
{
Graph->G[E->v1][E->v2] = E->weight;
}
MGraph BuildGraph()
{
MGraph Graph;
Edge E;
int Nv, i, v, u;
scanf_s("%d", &Nv); /* 读入顶点个数、边的个数 */
Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */
scanf_s("%d", &(Graph->Ne)); /* 读入边数 */
for (v = 0; v < Graph->Nv; v++) {
for (u = 0; u < Graph->Nv; u++) {
Graph->G[v][u] = -1;
}
}
if (Graph->Ne != 0) { /* 如果有边 */
E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点 */
/* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */
for (i = 0; i < Graph->Ne; i++) {
scanf_s("%d %d %d", &E->v1, &E->v2, &E->weight);
/* 注意:如果权重不是整型,Weight的读入格式要改 */
InsertEdge(Graph, E);
}
}
return Graph;
}
int max(int x, int y)
{
if (x > y) return x;
else return y;
}
bool TopSort(MGraph Graph) {
int Indegree[MaxVertexNum];//记录每个顶点的入度
int length[MaxVertexNum];
memset(length, 0, sizeof(length));
int v, u;
int cnt = 0;
int ans = 0;
//先把每个顶点的入度都标记为0
for (v = 0; v < Graph->Nv; v++) {
Indegree[v] = 0;
}
//遍历所有的边,如果存在这条有向边,那么末结点入度加1.
for (v = 0; v < Graph->Nv; v++) {
for (u = 0; u < Graph->Nv; u++) {
if (Graph->G[v][u] != -1) {
Indegree[u]++;
}
}
}
while (1) {
//如果一个图中,每个顶点的入度都大于0,则一定存在回路;如果不存在回路,就一定满足拓扑排序
//拓扑排序:先找到一个入度为0的点,输出,从图中删去该顶点和与该顶点相邻的边;重复这一过程直到所有的点都被输出
int flag = 0;
for (v = 0; v < Graph->Nv; v++) {
if (Indegree[v] == 0) {
Indegree[v] = -1;
flag = 1;
cnt++;
for (u = 0; u < Graph->Nv; u++) {
if (Graph->G[v][u] != -1) {
Indegree[u]--;
length[u] = max(length[u], length[v] + Graph->G[v][u]);
ans = max(ans, length[u]);
}
}
}
}
if (flag == 0) break;
}
if (cnt != Graph->Nv) printf("Impossible\n");
else printf("%d\n", ans);
return 0;
}