算法设计与分析——数塔问题(Java)

【问题】如下图(一个5层数塔)所示的一个数塔,从数塔的顶层出发,在每一个结点可以选择向左走或向右走,一直走到最底层,要求找出一条路径,使得路径上的数值和最大。例如,下图所示数塔的最大数值和是 8+15+9+10+18=60。

【想法】观察上图(一个5层数塔)所示数塔不难发现,从 5 层数塔的顶层(设顶层为第 1 层)出发,下一层选择向左走还是向右走取决于两个 4 层数塔的最大数值和,如下图(数塔问题的子问题具有重叠关系)所示,显然,子问题具有重叠的特征。

如何找到子问题满足的动态规划函数呢?显然,动态规划的求解需要从底层开始进行决策,图(一个5层数塔)所示数塔问题的决策过程如下图(数塔问题的决策过程)所示,具体过程如下。

求解初始子问题:底层的每个数字可以看作 1 层数塔,则最大数值和就是其自身,填写图(数塔问题的决策过程)最下一行。

再求解下一阶段的子问题:第 4 层的决策是在底层决策的基础上进行求解,可以看作 4 个 2 层数塔,如下图(数塔问题的动态规划求解过程)(a) 所示,对每个数塔进行求解,填写图(数塔问题的决策过程)的第 4 行。

再求解下一阶段的子问题:第 3 层的决策是在第 4 层决策的基础上进行求解,可以看作 3 个 2 层的数塔,如如下图(数塔问题的动态规划求解过程)(b) 所示,对每个数塔进行求解,填写图(数塔问题的决策过程)的第 3 行。 

依此类推,直到最后一个阶段:第 1 层的决策结果就是数塔问题的整体最优解。

由上述填表过程,可以设计数塔问题的存储结构。将给定的数塔存储为如下图(数塔的存储)所示

下三角矩阵 d[ n ][ n ],设二维数组 maxAdd[ n ][ n ] 存储动态规划每一步的决策结果,最后maxAdd[ 0 ][ 0 ]存储的就是数塔问题的最优解,则得到如下动态规划函数:

为了求得最大数值和的路径,设数组 path[ n ][ n ] 保存每一次决策所选择的数字在数组 d[ n ][ n ]中的列下标,例如,path[ i ][ j ] 的值表示在第 i 层第 j 个数塔的决策时选择的路径,path[ i ][ j ] 的值定义如下:

【算法】设函数 DataTower 完成 n 层数塔问题并输出对应的路径,算法用伪代码描述如下。

算法:数塔问题 DataTower

输入:二维数组 d[ n ][ n ]

输出:数塔的最大数值和及其路径

1. 初始化数组 maxAdd 的最后一行为数塔的底层数据:
    for(j=0;j<n; j++)
       maxAdd[ n - 1 ][ j ] = d[ n - 1 ][ j ];

2.从第 n-1 层开始直到第 1 层对下三角元素 maxAdd[ i ][ j ]执行下述操作:

    2.1 maxAdd[ i ][ j ] = d[ i ][ j ] + max{maxAdd[ i + 1 ][ j ],maxAdd[ i + 1 ][ j + 1 ]};

    2.2 如果选择下标 j 的元素,则 path[ i ][ j ] = j,否则 path[ i ][ j ] = j + 1; 

3.输出最大数值和 maxAdd[0][0];

4.根据 path 数组确定每一层决策的列下标,输出路径信息。

【算法分析】在上面算法中,步骤 1 的时间代价是O(n);步骤 2 进行填表工作,需填写 n - 1 行,由于数组 maxAdd 是下三角矩阵,第 i 行只需填写 i 个元素,因此,步骤 2 的时间代价是O(n^{2});由于数组 path 已经记载每个决策的列下标,步骤 4 只需输出每行的决策结果,因此,步骤 4 的时间代价是O(n)。算法的时间复杂性是O(n^{2})

【算法实现】设函数DataTower返回数塔的最大数值和,同时输出对应的路径,算法用JAVA语言描述如下:

public class DataTower {
    int n=5;
    public static void main(String[] args) {
        int n=5;
        DataTower dataTorwer = new DataTower();
        int [][] d = new int[n][n];
        d[0][0]=8;
        d[1][0]=12;d[1][1]=6;
        d[2][0]=3;d[2][1]=9;d[2][2]=4;
        d[3][0]=6;d[3][1]=5;d[3][2]=7;d[3][3]=8;
        d[4][0]=1;d[4][1]=2;d[4][2]=3;d[4][3]=4;d[4][4]=5;
        int max=dataTorwer.DataTower(d);
        System.out.println(max);
    }

    int DataTower( int d[][] )         //求解数塔问题,数塔存储在数组d[n][n]中
    {
        int [][] maxAdd = new int[n][n];
        int [][] path = new int[n][n];       //初始化
        int i, j;
        for (j = 0; j < n; j++)                         //初始化底层决策结果
            maxAdd[n-1][j] = d[n-1][j];
        for (i = n-2; i >= 0; i--)                       //进行第i层的决策
            for (j = 0; j <= i; j++)                 //填写addMax[i][j],只填写下三角
                if (maxAdd[i + 1][j]>maxAdd[i + 1][j + 1])
                {
                    maxAdd[i][j] = d[i][j] + maxAdd[i + 1][j];
                    path[i][j] = j;                  //本次决策选择下标j的元素
                }
                else
                {
                    maxAdd[i][j] = d[i][j] + maxAdd[i + 1][j + 1];
                    path[i][j] = j + 1;               //本次决策选择下标j+1的元素
                }
        System.out.println("路径为(顶层数字):"+d[0][0]);     //输出最顶层数字
        j = path[0][0];        //顶层决策是选择下一层列下标为path[0][0]的元素
        for (i = 1; i < n; i++)
        {
            System.out.println("下一层的元素为:");
            System.out.println(String.valueOf(d[i][j]));
            j = path[i][j];        //本层决策是选择下一层列下标为path[i][j]的元素
        }
        System.out.println("最大数值和为:");
        return maxAdd[0][0];               //返回最大数值和,即最终的决策结果
    }
}

运行结果如下:

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

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值