算法笔记_006:全源最短路径问题动态规划法

Posted 舞动的心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法笔记_006:全源最短路径问题动态规划法相关的知识,希望对你有一定的参考价值。

目录

1 问题描述

2 解决方案

2.1  动态规划法原理简介

2.2  具体编码

2.3  运行结果

 


1 问题描述

1)实验题目

   给定一个加权连通图(无向的或有向的),要求找出从每个定点到其他所有定点之间的最短路径以及最短路径的长度。

2)实验目的

  1)深刻掌握动态规划法的设计思想并能熟练运用,理解它与分治法的区别;

  2)掌握最优性原理和最优子结构性质;

  3)理解这样一个观点:用动态规划方法求解问题的关键在于确定动态规划函数的递推式。

3)实验要求

  1)实现Floyd算法;

  2)算法的输入可以手动输入,也可以自动生成;(PS:此处对于有向图的权重矩阵手动输入有点麻烦,故本文只实现已给定的权重矩阵值,计算最终结果)

  3)算法不仅要输出从每个顶点到其他所有顶点之间的最短路径,还有输出最短路径的长度;

  4)设计一个权重为负的图或有向图的例子,对于它,Floyd算法不能输出正确的结果。(PS:此处问题关键是,权重变为负值后,反而求取的结果为路径最长的路径结果)

 


2 解决方案

2.1  动态规划法原理简介

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式

与分治法最大的差别是适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)

2.2  具体编码

package com.liuzhen.floyd;

public class Floyd {

    //根据有向图的权重矩阵及起始的中间节点路径矩阵,返回最终图的距离矩阵及中间节点路径矩阵
    public static void getShortestPath(int[][] chart,int[][] path){
        int len = chart.length;
        //int k = 0;
        for(int k = 0;k < len;k++){  //k = 0表示,增加k = 0那一个中间顶点,k自增,表示后继增加第k个中间顶点
          for(int i = 0;i < len;i++){        
            for(int j = 0;j < len;j++){
                int temp = 0;   //新增一个中间顶点时,用该变量存储从i到k再到j的路径长度
                if(chart[i][k] != 100 && chart[k][j] != 100)  //当A[i][k]和A[k][j]路径都可以行走,100表示两顶点不相通
                    temp = chart[i][k] + chart[k][j];
//                System.out.println("中间顶点k值:"+k);
//                System.out.println("chart["+i+"]"+"["+k+"] = "+chart[i][k]);
//                System.out.println("chart["+k+"]"+"["+j+"] = "+chart[k][j]);
                if(chart[i][j] > temp  && temp != 0) {    //如果当前i到j路径长度(包括无法达到情况)大于以k为中间顶点的路径时
                    chart[i][j] = temp;
                    path[i][j] = k+1;   //当两顶点相通,且是最短路径时,把k+1存入中间节点路径矩阵path中
                }
            }  
          }
        }
    }
    
    //根据有向图的中间节点路径矩阵,以及两个顶点,返回这两个顶点之间的最短路径
    public static String getOneShortestPath(int[][] path,int start,int end){
        char startNode = (char) (‘a‘ + start);
        char endNode = (char) (‘a‘ + end);
        String nodePath = "";
        if(path[start][end] == 0){
            nodePath += startNode+"->"+endNode;
            return nodePath;
        }
        int middle = path[start][end]-1;
        //使用递归求解最短路径
        nodePath += getOneShortestPath(path,start,middle)+" , "+getOneShortestPath(path,middle,end);
        return nodePath;
    }
    
    //输出有向图所有顶点之间的最短路径及最短路径长度
    public static void printShortestPath(int[][] path,int[][] result){
        int len = path.length;
        for(int i = 0;i < len;i++){    
            char startNode = (char) (‘a‘ + i);
            for(int j = 0;j < len;j++){
                char endNode = (char) (‘a‘ + j);
                String ijPath = startNode+"——>"+endNode+"最短路径为:";
                String nodePath = getOneShortestPath(path,i,j);    
                System.out.println(ijPath+nodePath+" 。 路径长度为:"+result[i][j]);
                }        
        }
    }
    
    public static void main(String args[]){
        /*chart数组中,数组0,1,2,3等表示两顶点之间的权重值,即两顶点间的距离大小, 100表示两顶点不相通*/     
        int[][] chart = {{0,100,3,100},{2,0,100,100},{100,7,0,1},{6,100,100,0}};
        System.out.println("有向图chart的权重矩阵为(PS:其中值为100表示无穷大,即无法相通的路径):");
        System.out.println("\t"+"a"+"\t"+"b"+"\t"+"c"+"\t"+"d");
        for(int i = 0;i < 4;i++){
            char startNode = (char) (‘a‘ + i);
            System.out.print(startNode+"\t");
            for(int j = 0;j < 4;j++)
                System.out.print(chart[i][j]+"\t");
            System.out.println();    
        }
        /*path数组中,0表示两顶点相通,1表示两顶点间有一个中间节点a,2表示 两顶点间有一个中间节点b,3两顶点间有一个中间节点c,4两顶点间有一个中间节点d.
         * 100表示两顶点不相通*/
        int[][] path = {{0,100,0,100},{0,0,100,100},{100,0,0,0},{0,100,100,0}};
        getShortestPath(chart,path);    
        System.out.println("有向图chart的距离矩阵为:");
        System.out.println("\t"+"a"+"\t"+"b"+"\t"+"c"+"\t"+"d");
        for(int i = 0;i < 4;i++){
            char startNode = (char) (‘a‘ + i);
            System.out.print(startNode+"\t");
            for(int j = 0;j < 4;j++)
                System.out.print(chart[i][j]+"\t");
            System.out.println();    
        }
        System.out.println("有向图chart的中间节点路径矩阵为(PS:值为0表示两节点直接相通,值为1表示两节点有一个中间节点a,值为2表示中间节点为b,依次类推):");
        System.out.println("\t"+"a"+"\t"+"b"+"\t"+"c"+"\t"+"d");
        for(int i = 0;i < 4;i++){
            char startNode = (char) (‘a‘ + i);
            System.out.print(startNode+"\t");
            for(int j = 0;j < 4;j++)
                System.out.print(path[i][j]+"\t");
            System.out.println();    
        }
        System.out.println("最终求取结果为:");
        printShortestPath(path,chart);
        
        
    }
}

2.3  运行结果

有向图chart的权重矩阵为(PS:其中值为100表示无穷大,即无法相通的路径):
    a    b    c    d
a    0    100    3    100    
b    2    0    100    100    
c    100    7    0    1    
d    6    100    100    0    
有向图chart的距离矩阵为:
    a    b    c    d
a    0    10    3    4    
b    2    0    5    6    
c    7    7    0    1    
d    6    16    9    0    
有向图chart的中间节点路径矩阵为(PS:值为0表示两节点直接相通,值为1表示两节点有一个中间节点a,值为2表示中间节点为b,依次类推):
    a    b    c    d
a    0    3    0    3    
b    0    0    1    3    
c    4    0    0    0    
d    0    3    1    0    
最终求取结果为:
a——>a最短路径为:a->a 。 路径长度为:0
a——>b最短路径为:a->c , c->b 。 路径长度为:10
a——>c最短路径为:a->c 。 路径长度为:3
a——>d最短路径为:a->c , c->d 。 路径长度为:4
b——>a最短路径为:b->a 。 路径长度为:2
b——>b最短路径为:b->b 。 路径长度为:0
b——>c最短路径为:b->a , a->c 。 路径长度为:5
b——>d最短路径为:b->a , a->c , c->d 。 路径长度为:6
c——>a最短路径为:c->d , d->a 。 路径长度为:7
c——>b最短路径为:c->b 。 路径长度为:7
c——>c最短路径为:c->c 。 路径长度为:0
c——>d最短路径为:c->d 。 路径长度为:1
d——>a最短路径为:d->a 。 路径长度为:6
d——>b最短路径为:d->a , a->c , c->b 。 路径长度为:16
d——>c最短路径为:d->a , a->c 。 路径长度为:9
d——>d最短路径为:d->d 。 路径长度为:0

 

参考资料:

      1、http://www.360doc.com/content/13/0601/00/8076359_289597587.shtml

以上是关于算法笔记_006:全源最短路径问题动态规划法的主要内容,如果未能解决你的问题,请参考以下文章

Bellman-ford 单源最短路径算法

图论_最短路径

贪心算法—单源最短路径

Luogu5905 模板Johnson 全源最短路

[C++]单源最短路径:迪杰斯特拉(Dijkstra)算法(贪心算法)

leetcode之最短路径+记忆化dfs+bfs+动态规划刷题总结