【PTA数据结构 | C语言版】求图中关键活动

本专栏持续输出数据结构题目集,欢迎订阅。

文章目录

    • 题目
    • 代码

题目

请编写程序,实现求带权的有向图中关键活动的算法。

输入格式:
输入首先在第一行给出两个正整数,依次为当前要创建的图的顶点数 n(≤100)和边数 m。
随后 m 行,每行给出一条有向边的起点编号、终点编号、权重。顶点编号从 0 开始,权重(≤100)为整数。同行数字均以一个空格分隔。

输出格式:
按格式 输出关键活动,其中 u 为起点编号,v 为终点编号。按起点编号的升序,每行输出一个活动。起点编号相同时,与输入的顺序相反,即先输入的边后输出。
最后一行输出 关键路径分析结果为 x,其中 x 为 1 表示关键路径已成功求出,为 0 表示不成功。

输入样例 1:
9 12
0 1 5
0 2 3
1 4 4
1 5 2
2 3 6
2 4 1
3 6 7
4 6 3
4 7 5
5 7 6
6 8 2
7 8 8

输出样例 1:
<0, 1>
<1, 4>
<4, 7>
<7, 8>
关键路径分析结果为 1

输入样例 2:
8 9
0 2 3
0 4 5
1 2 12
1 4 7
3 4 9
4 5 2
4 6 1
5 7 2
7 4 41

输出样例 2:
关键路径分析结果为 0

代码

#include 
#include 
#include 

#define MAX_VERTICES 100
#define INF 999999999

typedef struct Node {
    int vertex;
    int weight;
    int input_order;  // 记录输入顺序
    struct Node* next;
} Node;

typedef struct {
    Node* head;
} AdjList;

typedef struct {
    int n, m;
    AdjList adj[MAX_VERTICES];
    int in_degree[MAX_VERTICES];
    int out_degree[MAX_VERTICES];
} Graph;

Node* createNode(int v, int w, int order) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->vertex = v;
    newNode->weight = w;
    newNode->input_order = order;
    newNode->next = NULL;
    return newNode;
}

Graph* createGraph(int n, int m) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->n = n;
    graph->m = m;
    for (int i = 0; i < n; i++) {
        graph->adj[i].head = NULL;
        graph->in_degree[i] = 0;
        graph->out_degree[i] = 0;
    }
    return graph;
}

void addEdge(Graph* graph, int src, int dest, int weight, int order) {
    Node* newNode = createNode(dest, weight, order);
    newNode->next = graph->adj[src].head;
    graph->adj[src].head = newNode;
    graph->in_degree[dest]++;
    graph->out_degree[src]++;
}

// 拓扑排序获取顶点顺序
int topologicalSort(Graph* graph, int* order) {
    int queue[MAX_VERTICES];
    int front = 0, rear = 0;
    int count = 0;
    int in_degree_copy[MAX_VERTICES];

    for (int i = 0; i < graph->n; i++) {
        in_degree_copy[i] = graph->in_degree[i];
        if (in_degree_copy[i] == 0) {
            queue[rear++] = i;
        }
    }

    while (front < rear) {
        int u = queue[front++];
        order[count++] = u;

        Node* current = graph->adj[u].head;
        while (current != NULL) {
            int v = current->vertex;
            in_degree_copy[v]--;
            if (in_degree_copy[v] == 0) {
                queue[rear++] = v;
            }
            current = current->next;
        }
    }

    return count == graph->n;
}

// 计算事件的最早发生时间
void computeVE(Graph* graph, int* order, int* ve) {
    for (int i = 0; i < graph->n; i++) {
        ve[i] = 0;
    }

    for (int i = 0; i < graph->n; i++) {
        int u = order[i];
        Node* current = graph->adj[u].head;
        while (current != NULL) {
            int v = current->vertex;
            int w = current->weight;
            if (ve[u] + w > ve[v]) {
                ve[v] = ve[u] + w;
            }
            current = current->next;
        }
    }
}

// 计算事件的最晚发生时间
void computeVL(Graph* graph, int* order, int* ve, int* vl) {
    int max_ve = 0;
    for (int i = 0; i < graph->n; i++) {
        if (ve[i] > max_ve) {
            max_ve = ve[i];
        }
    }

    for (int i = 0; i < graph->n; i++) {
        vl[i] = max_ve;
    }

    for (int i = graph->n - 1; i >= 0; i--) {
        int u = order[i];
        Node* current = graph->adj[u].head;
        while (current != NULL) {
            int v = current->vertex;
            int w = current->weight;
            if (vl[v] - w < vl[u]) {
                vl[u] = vl[v] - w;
            }
            current = current->next;
        }
    }
}

// 比较函数,用于按要求排序活动
int compare(const void* a, const void* b) {
    Node* nodeA = (Node*)a;
    Node* nodeB = (Node*)b;
    if (nodeA->vertex != nodeB->vertex) {
        return nodeA->vertex - nodeB->vertex;
    }
    return nodeB->input_order - nodeA->input_order;
}

int main() {
    int n, m;
    scanf("%d %d", &n, &m);

    Graph* graph = createGraph(n, m);

    for (int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        addEdge(graph, u, v, w, i);
    }

    int order[MAX_VERTICES];
    // 执行拓扑排序
    if (!topologicalSort(graph, order)) {
        printf("关键路径分析结果为 0\n");
        return 0;
    }

    int ve[MAX_VERTICES], vl[MAX_VERTICES];
    computeVE(graph, order, ve);
    computeVL(graph, order, ve, vl);

    // 检查是否存在汇点(出度为0且ve等于最大ve的顶点)
    int hasSink = 0;
    for (int i = 0; i < n; i++) {
        if (graph->out_degree[i] == 0 && ve[i] == vl[i]) {
            hasSink = 1;
            break;
        }
    }

    if (!hasSink) {
        printf("关键路径分析结果为 0\n");
        return 0;
    }

    // 收集所有活动并计算e和l
    Node activities[MAX_VERTICES * MAX_VERTICES];
    int count = 0;

    for (int u = 0; u < n; u++) {
        Node* current = graph->adj[u].head;
        while (current != NULL) {
            int v = current->vertex;
            int w = current->weight;
            int e = ve[u];
            int l = vl[v] - w;
            if (e == l) {
                activities[count].vertex = u;
                activities[count].weight = v;
                activities[count].input_order = current->input_order;
                count++;
            }
            current = current->next;
        }
    }

    // 按起点编号升序,起点相同时按输入顺序降序排序
    qsort(activities, count, sizeof(Node), compare);

    // 输出关键活动
    for (int i = 0; i < count; i++) {
        printf("<%d, %d>\n", activities[i].vertex, activities[i].weight);
    }

    printf("关键路径分析结果为 1\n");

    return 0;
}

你可能感兴趣的:(【PTA数据结构 | C语言版】求图中关键活动)