Bellman-Ford算法
Bellman-Ford算法的实现思路是通过不断迭代松弛操作来更新最短路径
松弛操作:不断更新最短路径和前驱结点的操作
三角形定理:两边之和大于第三边
任何图都满足三角形定理,因此可以求出最短的边
三角不等式是进行松弛操作的原理
dist[b]=min(dist[b],backup[a]+c);
适用情形:
- 可以处理带有负权边的图
- 实现通过m次迭代求出从起点到终点不超过m条边构成的最短路径。
算法实现
两层循环,内循环遍历所有边,外循环迭代有限的次数
外循环迭代次数:迭代了k次,那么该距离更新的是从1号点经过不超过k条边到n号点的最短距离
在内循环中遍历了所有的边,但是需要控制有限的次数,所以每次只能更新走一条边到达的距离
例:先更新了2号点,又用2号点更新了3号点,发生串联,此时就不能保证走了多少次了
防止串联:更新时只用上一次循环的结果,因此需要将dist[]每次备份一遍
例题 AcWing853. 有边数限制的最短路
题目大意:
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。请你求出 1 号点经过最多 k 条边到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。
数据范围 1 ≤ n , k ≤ 500 1 ≤ m ≤ 10000 任意边长的绝对值不超过 10000 。
思路:
开结构体存边,bellman_ford算法求出k次限制内到达的最短路径
两重循环,外循环记录次数,内循环遍历所有边
如果三角不等式成立,更新最短距离
Solved:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=510,M=1e5+10,INF=0x3f3f3f3f;
int n,m,k;
int dist[N];//最短路径
int backup[N];//备份上次更新结果,防止走多
//存边,bellman_ford存边比较随意,只要可以遍历到即可
struct op
{
int a,b,w;
}edge[M];
void bellman_ford()
{
//初始化
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
for(int i=1;i<=k;i++){
//每次循环只用上次循环结果
memcpy(backup,dist,sizeof(dist));
for(int j=1;j<=m;j++){
int a=edge[j].a;
int b=edge[j].b;
int w=edge[j].w;
//backup,防止多走一条边
dist[b]=min(dist[b],backup[a]+w);
}
}
//可能存在负权边,dist[N]更新时可能不==INF
if(dist[n]>INF/2){
cout<<"impossble"<<endl;
}else{
cout<<dist[n]<<endl;
}
return ;
}
signed main()
{
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
int a,b,w;cin>>a>>b>>w;
edge[i]={a,b,w};
}
bellman_ford();
return 0;
}