所有节点对最短路径

一些约定:用d(i,j)表示节点i到节点j的最短路径,w(i,j)表示节点i到节点j的权重;
对于n个节点的图,采用邻接矩阵的方式输入输出,输出及中间结果的矩阵也是n*n的矩阵,第i行j列表示从i到j的当前最短路径

我们这里讲解三个算法,第一个是利用传统的动态规划,第二个也是个动态规划算法,但是基于一种观察结果,他就是warshall算法,第三个算法是将问题转换为没有负数权重的图,再对每个节点调用Dijkstra算法,他就是Johnson算法。

注意:通常情况下我们只用warshall算法,其他两个算法一个效率太低,一个写起来太麻烦。

动态规划求所有节点对最短路径

适用条件:
没有负环(可有负权重)

动态规划步骤:
1.分析最优解的结构
(最短路径结构)
根据最短路径的最优子结构性质,有d(i,j) = d(i,k) + w(k,j)

2.递归定义最优解的值
(所有节点对最短路径问题的递归解)
首先要获取递归求解的对象表达式

这里采取最短路径的边数作为递归的计数对象,因为图中没有负环,所以对于边数大于n-1的最短路径计算也就没有意义(因为n-1条边已经能够将所有节点连接,一旦大于n-1条边则会形成环,并且所有的环都是正权重,所以会使结果变大),因此我们递归时控制边数m使其m>0 && m < n。m=1时,即为输入的邻接矩阵M。

在递归时,我们计算边数为m的最短路径时,将m-1的最短路径与d(i,j) = d(i,k) + w(k,j)进行比较,取最小值。因为k=j时,即为m-1的最短路径,因此问题简化为循环遍历k求d(i,j) = d(i,k) + w(k,j)的最小值。如下图公式:
公式
3.自底向上计算最优解的值
(自底向上计算最短路径权重)
因为含有m条边的距离是在m-1条边的基础上计算出来的,所以从含有一条边开始,自底向上求值,按照第二步所述编写代码,详见具体代码。

4.从计算出的最优解的值上构建最优解
按照正常步骤,按部就班的每次递归m++;这样最终的时间复杂度为O(n^4)。但我们要的是最终m=n-1的结果,而对中间结果不感兴趣,因此我们可以使m每次增加一倍。m>n-1时的结果与n-1时的结果相同,最终的时间复杂度被压缩到O(n^3 lgn)。(详见具体代码)

具体代码:

import java.util.Scanner;
/**
 * 
 * @author Founder
 *
 */
public class Main{
   
    private static final int INFINITE = 1000000000;
    public static void main(String[] args){
        int[][] matrix = initialize();
        print(getPairsPath(matrix));
    }

    public static int[][] initialize(){
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[][] data = new int[n][n];
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < n; ++j)
                data[i][j] = input.nextInt();
        return data;
    }

    public static int[][] extendPath(int[][] matrix){
        int[][] newMatrix = new int[matrix.length][matrix.length];
        for(int i = 0; i < matrix.length; ++i)
            for(int j = 0; j < matrix.length; ++j){
                newMatrix[i][j] = INFINITE;
                for(int k = 0; k < matrix.length; ++k)
                    if(matrix[i][k] + matrix[k][j] < newMatrix[i][j]){
                        newMatrix[i][j] = matrix[i][k] + matrix[k][j];
                    }
            }
        return newMatrix;
    }

    public static int[][] getPairsPath(int[][] matrix){
        int m = matrix.length;
        int i = 1;//i代表路径边数
        while(i < m - 1){
            matrix = extendPath(matrix);
            m *= 2;
        }
        return matrix;
    }

    public static void print(int[][] matrix){

        for(int i = 0; i < matrix.length; ++i)
            for(int j = 0; j < matrix.length; ++j){
                if(matrix[i][j] != INFINITE && i != j){
                    System.out.println("From " + i + " to " + j + " the cost is:" + matrix[i][j]);
                }
            }
    }

}

测试用例:
例子

输入:

5
0 3 8 1000000000 -4
1000000000 0 1000000000 1 7
1000000000 4 0 1000000000 1000000000
2 1000000000 -5 0 1000000000
1000000000 1000000000 1000000000 6 0

输出:

From 0 to 1 the cost is:1
From 0 to 2 the cost is:-3
From 0 to 3 the cost is:2
From 0 to 4 the cost is:-4
From 1 to 0 the cost is:3
From 1 to 2 the cost is:-4
From 1 to 3 the cost is:1
From 1 to 4 the cost is:-1
From 2 to 0 the cost is:7
From 2 to 1 the cost is:4
From 2 to 3 the cost is:5
From 2 to 4 the cost is:3
From 3 to 0 the cost is:2
From 3 to 1 the cost is:-1
From 3 to 2 the cost is:-5
From 3 to 4 the cost is:-2
From 4 to 0 the cost is:8
From 4
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值