08-图8 How Long Does It Take

106 阅读3分钟

Problem Description

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

Input Specification

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.

Output Specification

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

Sample Input 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

Sample Output 1

18

Sample Input 2

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

Sample Output 2

Impossible

Solution

题意理解:给定工程的 AOE 网络,求出工程完成的最早时间。

注意:工程有可能有多个开始项目和多个结束项目,要求输出整个工程完成的最短时间,也就是在所有结束项目完成的最早时间里输出最大的那个工程的最短时间

对于多个开始项目,入度为零的结点即为开始项目;对于结束项目,出度为零的结点即为结束项目。

在拓扑排序的基础上稍作修改来计算每一个结点的最早完成时间,思想是动态规划。

拓扑排序算法:

Pasted image 20220821231908.png

完整代码(详解在后面):

#include <stdio.h>
#include <stdlib.h>
#define INF 65535

void BuildGraph(int G[][100], int N, int M, int Indgree[], int Outdegree[])
{
    int i, j, a, b, c;
    for (i = 0; i < N; i++) {
        Indgree[i] = 0;
        Outdegree[i] = 0;
        for (j = 0; j < N; j++) {
            if (i == j) G[i][j] = 0;
            else G[i][j] = G[j][i] = INF;
        }
    }
    for (i = 0; i < M; i++) {
        scanf("%d%d%d", &a, &b, &c);
        G[a][b] = c;
        Indgree[b]++;
        Outdegree[a]++;
    }
}

void PrintMax(int N, int Outdegree[], int Earliest[])
{
    int i, MaxItem = Earliest[0];
    for (i = 0; i < N; i++) {
        if (Outdegree[i] == 0 && Earliest[i] > MaxItem)
            MaxItem = Earliest[i];
    }
    printf("%d", MaxItem);
}

void TopSort(int G[][100], int N, int Indgree[], int Outdegree[], int Earliest[])
{
    int front, rear, queue[100];
    int V, W, cnt;
    
    front = rear = 0;
    for (V = 0; V < N; V++) {
        Earliest[V] = 0;
        if (Indgree[V] == 0) {
            queue[rear++] = V;
        }
    }

    cnt = 0;
    while (front != rear) {
        V = queue[front++];
        cnt++;
        for (W = 0; W < N; W++) {
            if (Indgree[W] && G[V][W] < INF) {
                if (Earliest[V] + G[V][W] > Earliest[W])
                    Earliest[W] = Earliest[V] + G[V][W];
                if (--Indgree[W] == 0)
                    queue[rear++] = W;
            }
        }
    }

    if (cnt != N) printf("Impossible");
    else PrintMax(N, Outdegree, Earliest);
}

int main()
{
    int N, M;
    int Indgree[100], Outdegree[100], Earliest[100], G[100][100];
    scanf("%d%d", &N, &M);
    BuildGraph(G, N, M, Indgree, Outdegree);
    TopSort(G, N, Indgree, Outdegree, Earliest);
    return 0;
}

代码详解

程序框架:

  • 需要的变量
    • 邻接矩阵存图
    • 入度数组
    • 出度数组
    • 存最早时间的 dp 数组
  • 读入图
    • 矩阵对角线初始化为 0 ,其余初始化为 INF ,同时出入度都初始化为 0
    • 读入起点、终点、边权重,终点入度++,起点出度++
  • 拓扑排序输出结果
    • 初始化所有结点的 Earliest 为 0 ,同时将开始项目入队(入度为零的结点)
    • 计数器置零,进入循环,退出条件是队列为空
      • 队首元素出队,计数器++
      • 遍历队首元素的每一个后继结点(不包括自身)
        • 如果通过当前结点到达后继结点的时间比原来长,更新 Earliest
        • 后继结点入度 --(当前结点作为前导结点已经操作过了,删除)
        • 如果后继结点 -- 后入度为零,该后继结点入队
        • 继续遍历
    • 退出循环后,如果计数器发现出队的结点数小于网络总结点数,说明存在回路,无法完成项目,输出 impossible
    • 如果生成完了 Earliest 数组,输出所有结束项目中最早完成时间最大的时间。

Pasted image 20220821231947.png