0
点赞
收藏
分享

微信扫一扫

LeetCode 787. K 站中转内最便宜的航班(图/Bellman Ford算法)

zhyuzh3d 2022-04-16 阅读 74

文章目录


贝尔曼-福特算法(Bellman-Ford)

简介

贝尔曼-福特算法(Bellman-Ford)是由理查德·贝尔曼(Richard Bellman) 和 莱斯特·福特 创立的,求解含负权边的带权有向图的单源最短路径问题的一种算法。它的原理是对图进行 V − 1 V-1 V1松弛操作 V V V代表图中的点数),从源点逐次绕过其他顶点,以缩短到达终点的最短路径长度,得到不超过 V − 1 V-1 V1条边构成的最短路径。其优于迪科斯彻算法(Dijkstra)的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达 O ( V E ) O(VE) O(VE) E E E代表边数)。不能处理带负权边的无向图

Bellman-Ford算法的限制条件为:

  • 图中不能包含权值总和为负值的回路(负权值回路

算法思想

Bellman-Ford算法构造一个最短路径长度数组序列 d i s t 1 [ u ] , d i s t 2 [ u ] , ⋯ , d i s t n − 1 [ u ] dist^1[u],dist^2[u],\cdots,dist^{n-1}[u] dist1[u]dist2[u]distn1[u]。其中:

  • d i s t 1 [ u ] dist^1[u] dist1[u]为从源点 v v v到终点 u u u的只经过一条边的最短路径长度,并有 d i s t 1 [ u ] = e d g e [ v ] [ u ] dist^1[u]=edge[v][u] dist1[u]=edge[v][u]
  • d i s t 2 [ u ] dist^2[u] dist2[u]为从源点 v v v最多经过两条边到达终点 u u u的最短路径长度
  • d i s t 3 [ u ] dist^3[u] dist3[u]为从源点 v v v出发最多经过不构成负权值回路的三条边到达终点 u u u的最短路径长度
  • ⋯ \cdots
  • d i s t n − 1 [ u ] dist^{n-1}[u] distn1[u]为从源点 v v v出发最多经过不构成负权值回路的 n − 1 n-1 n1条边到达终点 u u u的最短路径长度

算法的最终目的是计算出 d i s t n − 1 [ u ] dist^{n-1}[u] distn1[u],为源点 v v v到顶点 u u u的最短路径长度

算法执行过程

在这里插入图片描述

a. 初始化操作,源点 s s s到自己的距离为 0 0 0,源点到其他点的距离为正 ∞ \infty

b. 第一次迭代,找到不超过1条边的最短路

c. 第二次迭代,找到不超过2条边的最短路

d. 第三次迭代,找到不超过3条边的最短路

e. 第四次迭代,找到不超过4条边的最短路

以此类推,可迭代次数是无数次的,但是如果不存在负环,那么当迭代次数与边数相等时就保证了结果

很重要的一点是每次迭代都是在上一次的基础上进行的,因此在代码实现时要注意保留上一次的结果,在上一次的基础上计算。

应用

题目描述

给定一个 n n n个点 m m m条边的有向图,图中可能存在重边和自环,边权可能为负数。

请你求出从 1 1 1号点到 n n n号点的最多经过 k k k条边的最短距离,如果无法从 1 1 1号点走到 n n n号点,输出 i m p o s s i b l e impossible impossible

注意:图中可能存在负权回路

输入格式

第一行包含三个整数 n , m , k n,m,k nmk

接下来 m m m行,每行包含三个整数 x , y , z x,y,z xyz,表示存在一条从点 x x x到点 y y y的有向边,边长为 z z z

输出格式

输出一个整数,表示从 1 1 1号点到 n n n号点的最多经过 k k k条边的最短距离。

如果不存在满足条件的路径,则输出“impossible”。

数据范围

1 ≤ n , k ≤ 500 1≤n,k≤500 1n,k500,

1 ≤ m ≤ 10000 1≤m≤10000 1m10000,

任意边长的绝对值不超过 10000 10000 10000

输入样例:

3 3 1

1 2 1

2 3 1

1 3 3

输出样例:

3

分析

因为存在可能存在负权边,所以Dijkstra算法无法完成。又因为可能存在负环,SPFA算法也无法完成。按道理来说,只要有负环且不限路径数,答案就可能是负无穷,Bellman-Ford算法也不能完成。但是本题给定了路径数k的限制,所以可以用Bellman-Ford算法来实现。

代码

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int k = sc.nextInt();
        // 邻接矩阵
        int[][] g = new int[n][n];
        final int INF = Integer.MAX_VALUE / 2;
        // 初始化
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                g[i][j] = (i == j) ? 0 : INF;
            }
        }
        // 存图
        for (int i = 0; i < m; i++) {
            int x = sc.nextInt() - 1;
            int y = sc.nextInt() - 1;
            int z = sc.nextInt();
            g[x][y] = z;
        }
		// dist[x] = y 代表从「源点/起点」到 x 的最短距离为 y
        int[] dist = new int[n];
        // 初始化源点到自己的距离为0,源点到其他点的距离为正无穷
        Arrays.fill(dist, INF);
        dist[0] = 0;
        // 迭代k次
        for (int limt = 0; limt < k; limt++) {
            int[] clone = dist.clone();
            // 更新最短距离
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    dist[j] = Math.min(dist[j], clone[i] + g[i][j]);
                }
            }
        }
        if (dist[n - 1] >= INF) {
            System.out.println("impossible");
        }
        System.out.println(dist[n - 1]);
    }
}
  • 时间复杂度为 O ( n m ) O(nm) O(nm)

LeetCode 787. K 站中转内最便宜的航班

题目描述

  • 787. K 站中转内最便宜的航班

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Bellman Ford + 邻接矩阵

本题中「限制最多经过不超过 k k k 个点」等价于「限制最多不超过 k + 1 k + 1 k+1 条边」,因此可以使用 Bellman Ford 来求解。

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        final int INF = Integer.MAX_VALUE / 2;
        int[][] g = new int[n][n];
        // 邻接矩阵初始化
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                g[i][j] = i == j ? 0 : INF;
            }
        }
        for (int[] f : flights) {
            g[f[0]][f[1]] = f[2];
        }
		// dist[x] = y 代表从「源点/起点」到 x 的最短距离为 y
        int[] dist = new int[n];
        // 初始化源点到自己的距离为0,源点到其他点的距离为正无穷
        Arrays.fill(dist, INF);
        dist[src] = 0;
        // 迭代k + 1次
        for (int limit = 0; limit < k + 1; limit++) {
            // 复制上一次的迭代结果
            int[] clone = dist.clone();
            // 更新最短距离
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    dist[j] = Math.min(dist[j], clone[i] + g[i][j]);
                }
            }
        }
        return dist[dst] >= INF ? -1 : dist[dst];
    }
}
  • 时间复杂度: O ( k ∗ n 2 ) O(k * n^2) O(kn2)
  • 空间复杂度: O ( n 2 ) O(n^2) O(n2)

Reference

  • 贝尔曼-福特算法

  • Bellman-Ford算法

  • 运用 Bellman Ford 求解有限制的最短路问题

举报

相关推荐

0 条评论