算法笔记_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

时间: 2024-10-15 05:43:14

算法笔记_006:全源最短路径问题【动态规划法】的相关文章

Floyd-Warshall 全源最短路径算法

Floyd-Warshall 算法采用动态规划方案来解决在一个有向图 G = (V, E) 上每对顶点间的最短路径问题,即全源最短路径问题(All-Pairs Shortest Paths Problem),其中图 G 允许存在权值为负的边,但不存在权值为负的回路.Floyd-Warshall 算法的运行时间为 Θ(V3). Floyd-Warshall 算法由 Robert Floyd 于 1962 年提出,但其实质上与 Bernad Roy 于 1959 年和 Stephen Warshal

Johnson 全源最短路径算法

解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括: Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负: Bellman-Ford 单源最短路径算法:时间复杂度为 O(VE),适用于带负权值情况: 对于全源最短路径问题(All-Pairs Shortest Paths Problem),可以认为是单源最短路径问题的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离.例如,对每个顶点应用 Bel

Dijkstra算法(求单源最短路径)

问题描述 单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径. 最短路径的最优子结构性质 该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径.下面证明该性质的正确性. 性质证明:用反证法易证. Dijkstra算法实现 ps:用连接矩阵int matrix[][]储存边长关系.int dist[2...n]  储存原点1到其他点(2,3...n)的最短距离的"估计值

全源最短路径 - floyd算法 - O(N ^ 3)

Floyd-Warshall算法的原理是动态规划. 设Di,j,k为从i到j的只以(1..k)集合中的节点为中间节点的最短路径的长度. 若最短路径经过点k,则Di,j,k = Di,k,k − 1 + Dk,j,k − 1: 若最短路径不经过点k,则Di,j,k = Di,j,k − 1. 因此,Di,j,k = min(Di,k,k − 1 + Dk,j,k − 1,Di,j,k − 1). for (k = 1; k <= n; k++) //经过编号为前k个的顶点 { for (i = 1

笔记:多源最短路径(FLOYD)

文中代码如下: #include <iostream> #include <cstdio> using namespace std; int e[10][10]; int main() { int k,i,j,n,m,t1,t2,t3; int inf=99999999; //用inf存储一个我们认为的正无穷值 cin>>n>>m; //读入n和m,n表示顶点个数,m表示边的条数 //初始化数组,其实就是初始化城市之间的路程 for(i=1;i<=n;

[C++] 多源最短路径(带权有向图):【Floyd算法(动态规划法)】 VS nX Dijkstra算法(贪心算法)

1 Floyd算法 1.1 Code /** * 弗洛伊德(Floyd)算法: 1 解决问题: 多源最短路径问题 求每一对顶点之间的最短路径 Background: 带权有向图 2 算法思想: 动态规划(DP, Dynamic Programming) 3 时间复杂度: O(n^3) */ #include<stdio.h> #include<iostream> using namespace std; // 1 定义图模型(邻接矩阵表示法)的基本存储结构体 # define Ma

单源最短路径(Dijkstra)——贪心算法

Dijkstra算法是解单源最短路径问题的贪心算法.其基本思想是,设置顶点集合点集合S并不断地做贪心选择来扩充这个集合.一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知.初始时,S中仅含有源.设u是G的其一顶点.把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组Distance记录当前每个顶点所对应的最短特殊路径长度.Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶占,Distance就记录了从源到所有其它顶点之间最短路径长度. 例如下图中的有向图,应用Dij

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

1 Dijkstra算法 1.1 算法基本信息 解决问题/提出背景 单源最短路径(在带权有向图中,求从某顶点到其余各顶点的最短路径) 算法思想 贪心算法 按路径长度递增的次序,依次产生最短路径的算法 [适用范围]Dijkstra算法仅适用于[权重为正]的图模型中 时间复杂度 O(n^3) 补充说明 亦可应用于[多源最短路径](推荐:Floyd算法(动态规划,O(n^3))) Dijkstra 时间复杂度:O(n^3) 1.2 算法描述 1.2.1 求解过程(具体思路) 1.2.2 示例 1.2

用小根堆实现dijkstra,求图的单源最短路径

小根堆实现dijkstra 求图的最短路径,最常用的有四种方法: 1.Floyed(弗洛伊德)算法.最简单的最短路径算法,可以求多源最短路径.时间复杂度为O(n*n*n). 2.Dijkstra(迪杰斯特拉)算法.只能求单源最短路径.时间复杂度为O(n*n). 3.Bellman-Ford(贝尔曼福德)算法.只能求单源最短路径.时间复杂度为O(NE)(N为顶点数,E是边数). 4.SPFA算法.为Bellman-Ford算法的队列实现,减少不必要的冗杂计算.只能求单源最短路径.时间复杂度为O(k