【PTA数据结构 | C语言版】最短路的交点

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

文章目录

    • 题目
    • 代码

题目

给定有向加权图 G,和 4 个顶点 u, v, s, t。假设图 G 中所有边的权值都非负。设计一个算法来判定“从 u 到 v 的最短路径”和“从 s 到 t 的最短路径”是否存在一个交点 w。也即,顶点 w 是 u 到 v 的最短路径上的一个顶点,同时也是 s 到 t 的最短路径上的一个顶点。
注意:最短路径包含两个端点;一对顶点间的最短路径可能不止一条,求交点时必须将所有最短路径考虑在内。

输入格式:
输入第 1 行给出 6 个正整数,依次为:n(≤1000,图中顶点数)、m(≤3000,图中边数)、题面中描述的 4 个顶点 u, v, s, t 的编号。这里假设顶点从 1 到 n 编号,并且 u, v, s, t 的编号均不相同。
随后 m 行,每行给出一条有向边的起点编号、终点编号、非负权重(≤100)。题目保证每条边都不会重复给出,同行数字间以空格分隔。

输出格式:
在一行中,按编号递增的顺序输出所有交点。数字间以 1 个空格分隔,行首尾不得有多余空格。如果交点不存在,则在一行中输出 No Intersection。

输入样例 1:
6 7 1 2 3 4
1 2 3
1 5 1
3 5 1
5 6 1
5 4 2
6 4 1
6 2 1

输出样例 1:
5 6

输入样例 2:
4 4 1 2 3 4
1 2 1
3 4 1
1 4 1
3 2 1

输出样例 2:
No Intersection

代码

#include 
#include 
#include 

#define MAX_N 1001
#define INF 0x3f3f3f3f

// 邻接表节点结构
typedef struct Edge {
    int to;          // 目标顶点
    int weight;      // 边权重
    struct Edge* next; // 下一条边
} Edge;

// 添加边到邻接表
void addEdge(Edge* adj[], int from, int to, int weight) {
    Edge* newEdge = (Edge*)malloc(sizeof(Edge));
    newEdge->to = to;
    newEdge->weight = weight;
    newEdge->next = adj[from];
    adj[from] = newEdge;
}

// Dijkstra算法计算单源最短路径
void dijkstra(Edge* adj[], int n, int start, int dist[]) {
    int visited[MAX_N] = {0};
    for (int i = 1; i <= n; i++) {
        dist[i] = INF;
    }
    dist[start] = 0;
    
    for (int i = 1; i <= n; i++) {
        // 找到未访问的距离最近的顶点
        int u = -1;
        int minDist = INF;
        for (int j = 1; j <= n; j++) {
            if (!visited[j] && dist[j] < minDist) {
                minDist = dist[j];
                u = j;
            }
        }
        
        if (u == -1) break; // 所有可达顶点都已处理
        visited[u] = 1;
        
        // 更新邻接顶点的距离
        Edge* edge = adj[u];
        while (edge != NULL) {
            int v = edge->to;
            int w = edge->weight;
            if (dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
            }
            edge = edge->next;
        }
    }
}

// 构建反向图
void buildReverseGraph(Edge* adj[], Edge* reverseAdj[], int n) {
    for (int u = 1; u <= n; u++) {
        Edge* edge = adj[u];
        while (edge != NULL) {
            int v = edge->to;
            int w = edge->weight;
            addEdge(reverseAdj, v, u, w);
            edge = edge->next;
        }
    }
}

// 广度优先搜索获取所有在最短路径上的节点
void bfs(Edge* adj[], int n, int start, int end, int dist[], int inPath[]) {
    int queue[MAX_N];
    int front = 0, rear = 0;
    
    // 终点一定在路径上
    queue[rear++] = end;
    inPath[end] = 1;
    
    while (front < rear) {
        int v = queue[front++];
        
        // 遍历所有能到达v的顶点u
        Edge* edge = adj[v];
        while (edge != NULL) {
            int u = edge->to;
            int w = edge->weight;
            
            // 如果u到end的最短路径经过v
            if (dist[u] + w == dist[v] && !inPath[u]) {
                inPath[u] = 1;
                queue[rear++] = u;
            }
            
            edge = edge->next;
        }
    }
}

int main() {
    int n, m, u, v, s, t;
    scanf("%d %d %d %d %d %d", &n, &m, &u, &v, &s, &t);
    
    // 构建原图和反向图的邻接表
    Edge* adj[MAX_N] = {NULL};
    Edge* reverseAdj[MAX_N] = {NULL};
    
    for (int i = 0; i < m; i++) {
        int from, to, weight;
        scanf("%d %d %d", &from, &to, &weight);
        addEdge(adj, from, to, weight);
    }
    
    // 构建反向图用于反向搜索
    buildReverseGraph(adj, reverseAdj, n);
    
    // 计算4个单源最短路径
    int dist_u[MAX_N], dist_s[MAX_N];
    dijkstra(adj, n, u, dist_u);  // u到所有顶点的最短距离
    dijkstra(adj, n, s, dist_s);  // s到所有顶点的最短距离
    
    // 检查是否存在有效路径
    if (dist_u[v] == INF || dist_s[t] == INF) {
        printf("No Intersection\n");
        return 0;
    }
    
    // 找出u到v的所有最短路径上的顶点
    int in_uv[MAX_N] = {0};
    bfs(reverseAdj, n, u, v, dist_u, in_uv);
    
    // 找出s到t的所有最短路径上的顶点
    int in_st[MAX_N] = {0};
    bfs(reverseAdj, n, s, t, dist_s, in_st);
    
    // 收集所有交点
    int intersections[MAX_N];
    int count = 0;
    
    for (int w = 1; w <= n; w++) {
        if (in_uv[w] && in_st[w]) {
            intersections[count++] = w;
        }
    }
    
    // 输出结果
    if (count == 0) {
        printf("No Intersection\n");
    } else {
        for (int i = 0; i < count; i++) {
            if (i > 0) printf(" ");
            printf("%d", intersections[i]);
        }
        printf("\n");
    }
    
    // 释放内存
    for (int i = 1; i <= n; i++) {
        Edge* edge = adj[i];
        while (edge != NULL) {
            Edge* temp = edge;
            edge = edge->next;
            free(temp);
        }
        
        edge = reverseAdj[i];
        while (edge != NULL) {
            Edge* temp = edge;
            edge = edge->next;
            free(temp);
        }
    }
    
    return 0;
}

你可能感兴趣的:(【PTA数据结构 | C语言版】最短路的交点)