/*
BellmanFord 算法用于解决单源最短路径问题(相比迪杰斯特拉,它可以解决带负权值的有向图)
BellmanFord 算法一共会遍历 n-1 次,每次都会对所有的边进行一次松弛操作,因此时间复杂度是 N * E
迭代次数是有实际意义的:比如一共迭代了 k 次,那么此时求的是从源点到第 i 个节点,经过的边不超过k条时的最短距离
从源点到其他顶点不超过一条边时的最短距离;从源点到其他顶点不超过两条边时的最短距离,以此类推依此类推
一共 n - 1 次循环,每次循环对所有的边进行一遍松弛操作:
对所有边进行一次松弛操作,就求出了从源点到所有点,经过的边数最多为 1 的最短路径
对所有边进行两次松弛操作,就求出了源点到所有点,经过的边数最多为 2 的最路径
....
对所有边进行 n-1 次松弛操作,就求出了源点到所有点,经过的边数最多为 n-1 的最短路径
例题: ACWing 853 有边数限制的最短路
*/
public class Main {
static class Edge{
int from;
int to;
int weight;
public Edge(int from, int to, int weight){
this.from = from;
this.to = to;
this.weight = weight;
}
}
public static final int N = 10000; // 表示不可达
public static void main(String[] args) {
int[][] map = {
{N, -3, N, N, 5},
{N, N, 2, N, N},
{N, N, N, 3, N},
{N, N, N, N, 2},
{N, N, N, N, N}
};
bellmanFord(map, 0);
}
// 求从 source 到其他结点的最短路径长度。
public static void bellmanFord(int[][] map, int source) {
List<Edge> edges = new ArrayList<>(); // 存放图中的所有边
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map.length; j++){
if(map[i][j] != N){
edges.add(new Edge(i, j, map[i][j]));
}
}
}
int[] shortest = new int[map.length]; // 存储 source 到 i 的最短路径长度,初始化时 source 到其它顶点之间的距离为无穷大
Arrays.fill(shortest, N);
shortest[source] = 0;
/* 一共 n - 1 次循环,每次循环对所有的边进行一遍松弛操作
对所有边进行一次松弛操作,就求出了从源点到所有点,经过的边数最多为 1 的最短路径
对所有边进行两次松弛操作,就求出了源点到所有点,经过的边数最多为 2 的最路径
....
对所有边进行 n-1 次松弛操作,就求出了源点到所有点,经过的边数最多为 n-1 的最短路径
迭代次数是有实际意义的:比如一共迭代了 k 次,那么此时求的是从源点到第 i 个节点,经过的边不超过k条时的最短距离
从源点到其他顶点不超过一条边时的最短距离;从源点到其他顶点不超过两条边时的最短距离,以此类推依此类推
*/
for (int i = 1; i < map.length; i ++ ) {
int[] back = Arrays.copyOf(shortest, shortest.length); // 防止串联的情况发生,如:v1->v2权值1 v1->v3权值3 v2->v3权值1
for (Edge edge : edges) {
int from = edge.from, to = edge.to, weight = edge.weight;
if (shortest[to] > back[from] + weight){
shortest[to] = back[from] + weight;
}
}
}
boolean flag = false;
// 判断给定图中是否存在负权值环。如果在第n次迭代的时候仍然会去松弛某条边,那么就说明图中有源点可达的负环
for (Edge edge : edges) {
int from = edge.from, to = edge.to, weight = edge.weight;
if (shortest[to] > shortest[from] + weight){
flag = true;
break;
}
}
if(flag){
System.out.println("图中存在负权重环");
}else{
System.out.println(Arrays.toString(shortest));
}
}
}
BellmanFord算法
于 2023-09-18 03:34:50 首次发布