本专栏持续输出数据结构题目集,欢迎订阅。
给定有向加权图 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;
}