算法设计与分析——多段图的最短路径问题(Java)

【问题】设图 G = (V,E) 是一个带权有向图,如果把顶点集合 V 划分成 k 个互不相交的子集V_{i}(2 ≤ k ≤ n,1 ≤ i ≤ k),使得 E 中的任何一条边 <u,v>,必有 u \in V,v \in V_{i+m}( 1 ≤ i < k,1 < i+m ≤ k),则称图 G 为多段图,称 s \in V_{1} 为源点,t \in V_{k} 为终点。多段图的最短路径问题 (multi-segment graph shortest path problem) 求从源点到终点的最小代价路径。

【想法】由于多段图将顶点划分为 k 个互不相交的子集,所以,可以将多段图划分为 k 段,每一段包含顶点的一个子集,根据多段图的定义,,每个子集中的顶点互不邻接。不失一般性,将多段图的顶点按照段的顺序进行编号,同一段内顶点的顺序无关紧要。假设图中的顶点个数为 n,则源点  s 的编号为 0,终点t的编号为 n-1,并且对图中的任何一条边<u,v>,顶点 u 的编号小于顶点 v 的编号。下图(一个多段图)所示是一个含有10个顶点的多段图。

首先证明多段图的最短路径问题满足最优性原理。设s,s_{1},s_{2},...,s_{p},t 是从 s 到 t 的一条最短路径,从源点 s 开始,设从 s 到下一段的顶点 s_{1} 已经求出,则问题转化为求从 s 到 t 的最短路径,显然 s,s_{1},s_{2},...,s_{p},t 一定构成一条从 s_{1} 到 t 的最短路径,如若不然,设 s,s_{1},r_{1},r_{2},...,r_(q),t 是一条从 s_{1} 到 t 的最短路径,则 s,s_{1},r_{1},r_{2},...,r_(q),t 将是一条从 s 到 t 的路径且比 s,s_{1},s_{2},...,s_{p},t 的路径长度要短,从而导致矛盾。所以,多段图的最短路径问题满足最优性原理。
如何定义子问题呢?设 c_{w} 表示多段图的有向边 < u,v > 上的权值,将从源点 s 到终点 t 的最短路径长度记为 d(s,t),考虑原问题的部分解 d(s,t),显然有下式成立:

对于上图(一个多段图)所示多段图,动态规划法求解最短路径的填表过程如下图(多段图最短路径问题的填表过程)所示:

具体过程如下(括号中给出了决策产生的状态转移)。

首先求解初始子问题,可直接获得:

再求解下一阶段的子问题,有:

再求解下一阶段的子问题,有:

 直到最后一个阶段有:

再将状态进行回溯,得到最短路径为0→3→5→8→9,最短路径长度为16。 

【算法】设数组 cost[ n ] 存储最短路径长度,cost[ j ] 表示从源点 s 到顶点 j 的最短路径长度,数组 path[ n ] 记录状态转移,path[ j ] 表示从源点 s 到顶点 j 的路径上顶点 j 的前一个顶点,动态规划法求解多段图的最短路径问题的算法如下。

算法:多段图的最短路径问题

输入:多段图的代价矩阵

输出:最短路径长度及路径 c[ n ][ n ]

1.循环变量 j 从1 ~ n-1 重复下述操作,执行填表工作:

    1.1 考察顶点 j 的所有入边,对于边 < i,j >∈ E,执行下述操作:

        1.1.1 cost[ j ] = min{ cost[ i ] + c[ i ][ j ] };

        1.1.2 path[ j ] = 使 cost[ i ] + c[ i ][ j ]最小的 i;

    1.2  j++;

2.输出最短路径长度 cost[ n - 1 ];

3.循环变量 i = path[ n - 1],循环直到path[ i - ] = 0,输出最短路径经过的顶点:

    3.1 输出path[ i ];

    3.2 i = path[ i ]。

【算法分析】算法多段图的最短路径问题主要由两部分组成:第一部分是依次计算从源点到各个顶点的最短路径长度,由两层嵌套的循环组成,外层循环执行 n - 1 次,内层循环对所有入边进行计算,并且在所有循环中,每条人边只计算一次。假定图的边数为 m,则时间性能是 O(m) 第二部分是输出最短路径经过的顶点,设多段图划分为 k 段,其时间性能是O(k)。所以,算法多段图的最短路径问题的时间复杂性为O(m+k)

【算法实现】设多段图中 n 个顶点的编号为{0,1,…,n-1},数组 arc[ n ][ n ]存储边上的权值,为避免传递参数,将 arc[ n ][ n ]设为全局变量。设数组 cost[ n ] 存储最短路径长度,数组 path[ n ]记录状态转移。设函数 CreatGraph 建立多段图的代价矩阵并返回图的顶点个数,函数 BackPath 求得多段图的最短路径并返回其长度,算法用JAVA语言描述如下。

import java.util.Scanner;

public class creatGraph {
    int N = 20;
    int MAX = 1000;
    int [][]arc = new int[N][N];
    public static void main(String[] args) {
        creatGraph creatgraph =new creatGraph();
        int n = creatgraph.creatGraph( );
        int pathLen = creatgraph.Backpath(n);
        System.out.println("最短路径的长度是:"+pathLen);
    }

    int creatGraph()
    {
        int i, j, k;
        int weight;
        int vnum,arcnum;
        System.out.println("请输入顶点的个数和边的个数:");
        Scanner sc = new Scanner(System.in);
        vnum=sc.nextInt();
        arcnum =sc.nextInt();
        for (i = 0; i < vnum; i++)            //初始化图的代价矩阵
            for (j = 0; j < vnum; j++)
                arc[i][j] = MAX;
        for (k = 0; k < arcnum; k++)        //建立图的代价矩阵
        {
            System.out.println("请输入边的两个顶点和权值:");
            i= sc.nextInt();
            j= sc.nextInt();
            weight= sc.nextInt();
            arc[i][j] = weight;
        }
        return vnum;                 //返回顶点个数
    }
    int Backpath(int n)                  //求n个顶点的多段图的最短路径
    {
        int i, j, temp;
        int [] cost=new int[N];
        int [] path=new int[N];        //存储路径长度和路径
        for(i = 0; i < n; i++)           //初始化路径长度为∞
        {
            cost[i] = MAX;
            path[i] = -1;
        }
        cost[0] = 0;                   //顶点0为源点
        for(j = 1; j < n; j++)         //执行填表工作
        {
            for(i = j - 1; i >= 0; i--)    //考察所有入边
            {
                if (arc[i][j] + cost[i] < cost[j])
                {
                    cost[j] = arc[i][j] + cost[i];
                    path[j] = i;
                }
            }
        }
        System.out.println(n-1);              //输出终点
        i = n-1;
        while (path[i] >= 0)                   //依次输出path[i]
        {
            System.out.println("<-"+path[i]);
            i = path[i];                      //求得路径上顶点i的前一个顶点
        }
        System.out.println();
        return cost[n-1];                 //返回最短路径长度
    }

}

from:算法设计与分析(第2版)——王红梅 胡明 编著——清华大学出版社

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值