【题目来源】
https://www.luogu.com.cn/problem/P1339
【题目描述】
有一个 n 个点 m 条边的无向图,请求出从 s 到 t 的最短路长度。
【输入格式】
第一行四个正整数 n,m,s,t。 接下来 m 行,每行三个正整数 u,v,w,表示一条连接 u,v,长为 w 的边。
【输出格式】
输出一行一个整数,表示答案。
【输入样例】
7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1
【输出样例】
7
【数据范围】
对于 100% 的数据,1≤n≤2500,1≤m≤6200,1≤w≤1000。
【算法分析】
● Bellman-Ford 算法使用边集数组存图,而非邻接表。这是因为 Bellman-Ford 在每一轮迭代中,都需要遍历图中全部边执行松弛操作,无需查询某个顶点的出边。而邻接表的核心优势,是快速获取单个顶点的邻接边,但这项能力在 Bellman-Ford 算法中完全用不到。因此,邻接表额外的索引结构自然成为冗余。反观边集数组,它仅存储每条边自身的信息,结构极简,恰好适配 Bellman-Ford 算法的执行逻辑。
● 包含 n 个顶点的图,其最短路径一定是简单路径(路径中不会重复经过同一个顶点,不含任何环),即最多包含 n-1 条边。所以,Bellman-Ford 算法最多只需要松弛 n-1 轮。
(1)算法的第 k 轮松弛,作用是求出“最多经过 k 条边”能够得到的最短距离。第 1 轮更新仅用 1 条边可达的最短路,第 2 轮更新最多 2 条边的最短路,以此类推。当完成 n-1 轮松弛后,所有简单路径对应的最短距离都已经被更新完成。
(2)如果执行完 n-1 轮之后,仍然还有边可以继续松弛,就说明图中存在“负环”。即可以不断环绕这个环,无限降低路径总权值,不存在有限的最短路径。
● 本题为无向图。无向边 u-v 等价于两条方向相反的有向边:u → v 与 v → u。因此在使用 Bellman‑Ford 算法的边集数组存图时,读入一条无向边,需要同时存入这两条有向边,才能完整表达双向连通关系。
【算法代码】
#include <bits/stdc++.h> using namespace std; const int N=3e3+5; const int M=2e4+5; const int inf=0x3f3f3f3f; int dis[N]; struct edge { int u,v,w; } e[M]; int n,m,s,t; void bellman() { memset(dis,inf,sizeof dis); dis[s]=0; for(int i=1; i<=n-1; i++) { bool flag=0; for(int j=1; j<=2*m; j++) { int u=e[j].u,v=e[j].v,w=e[j].w; if(dis[u]!=inf && dis[v]>dis[u]+w) { dis[v]=dis[u]+w; flag=1; } } if(!flag) break; } } int main() { cin>>n>>m>>s>>t; int cnt=0; for(int i=1; i<=m; i++) { int u,v,w; cin>>u>>v>>w; e[++cnt]= {u,v,w}; e[++cnt]= {v,u,w}; } bellman(); cout<<dis[t]; return 0; } /* in: 7 11 5 4 2 4 2 1 4 3 7 2 2 3 4 3 5 7 5 7 3 3 6 1 1 6 3 4 2 4 3 5 6 3 7 2 1 out: 7 */
【参考文献】
https://www.luogu.com.cn/problem/solution/P1339