算法原理
松弛操作:这是 Bellman - Ford 算法的核心操作。对于每条边u→v,如果从源点s到u的已知最短距离加上边u→v的权重小于当前已知的从s到v的最短距离,那么就更新从s到v的最短距离为从s到u的最短距离加上边u→v的权重。即d[v]=min(d[v],d[u]+w(u,v)),其中d[v]表示从源点到顶点v的最短距离,w(u,v)表示边u→v的权重。
迭代过程:算法对图中的所有边进行n−1次松弛操作,其中n是图中顶点的数量。这是因为在一个没有负权回路的有向图中,从源点到任何一个顶点的最短路径最多包含n−1条边。每次迭代都尝试通过已有的最短路径信息来更新其他顶点的最短距离,经过n−1次迭代后,就可以得到从源点到所有其他顶点的最短路径长度。
算法实现步骤
1.初始化:将源点s到自身的距离设置为0,即d[s]=0,将其他所有顶点到源点的距离设置为无穷大∞。
2.迭代松弛:进行n−1次迭代,每次迭代都对图中的所有边进行松弛操作。
3.检查负权回路:在完成n−1次迭代后,再对所有边进行一次检查。如果仍然存在可以松弛的边,即存在边u→v使得d[v]>d[u]+w(u,v),则说明图中存在负权回路,算法无法给出正确的最短路径,返回错误信息。否则,算法成功完成,d[v]中存储的就是从源点s到顶点v的最短距离。
题目:
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出从 1号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible
。
注意:图中可能 存在负权回路 。
输入格式
第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y的有向边,边长为 z。
点的编号为 1∼n。
输出格式
输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。
如果不存在满足条件的路径,则输出 impossible
。
数据范围
1≤n,k≤500,
1≤m≤10000,
1≤x,y≤n,
任意边长的绝对值不超过 10000。
输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N =510,M=10010;
int dist[N],backup[N];
struct Edges{
int a,b,w;
}edges[M];//用结构体存储每条边,w表示权重
int n,m,k;
void bellman_ford(){
memset(dist,0x3f,sizeof dist);//初始化,使到每个点的最短距离都为无穷大
dist[1]=0;
for(int i=0;i<k;i++){
memcpy(backup,dist,sizeof dist);//每次更新是在前一次的基础上,所以首先要复制前一次最短路
for(int j=0;j<m;j++){
int a=edges[j].a,b=edges[j].b,w=edges[j].w;
dist[b]=min(dist[b],backup[a]+w);更新边的长度
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i]={a,b,w};
}
bellman_ford();
if(dist[n]>0x3f3f3f3f/2)printf("impossible");
else printf("%d",dist[n]);
return 0;
}
注:判断满足条件的最短路时,使用的判断条件为dist[n] > 0x3f3f3f3f /2,而不是dist[n]==0x3f3f3f3f。原因是:0x3f3f3f3f事实上是一个确定的值而不是真正的无穷大,当一个距离为无穷大的点加上一个负的权重时,答案可能会被错误的更新。