动态规划——数塔问题(三维数组的应用)

本文介绍了如何使用动态规划方法解决数塔问题,通过自下而上的决策过程,逐步降低问题阶数,最终找到最优解。文章详细解释了理论背景,给出了算法设计以及C语言代码实现,并展示了运行结果。
摘要由CSDN通过智能技术生成

 一、例题要求及理论分析

声明:理论指导《算法设计与分析 第四版》

因为这个地方用到了三维数组,感觉很有意思就故意挑出来分享给大家(三维数组可以看成很多页二维数组)

4.5.1认识动态规划
数塔问题:
如图4-12所示的一个数塔,从顶层到底层或从底层到顶层,在每一结点可以选择向左走或是向右走,要求找出一条路径,使路径上的数值和最大。
问题分析:
(1)不难理解,这个问题用贪婪算法有可能会找不到真正的最大和。以图4-12为例就是如此。采用贪婪策略,无论是自上面下,还是自下而上,每次向下都选择较大的一个数移动,则路径和分别为:

                                 数塔图
9+15+8+9+10=51(自上而下),19+2+10+12+9=52(自下而上)
都得不到最优解,真正的最和是:
9+12+10+18+10=59
(2)要找到最大和的前提条件是,要能看到数塔的全貌,下面的算法设计都是以此为前提的。
在知道数塔全貌的前提下,可以用枚举法或第5章将学习的搜索算法来解决问题。但从图中可以看出,在数塔层数为 n 时,要枚举的路径为2^(n-1)条。在 n 稍大的情况下,需要列举出的路径条数是一个非常庞大的数目。所以枚举法也不是一个适合此问题的算法策略。
(3)这个问题的原始数据是一个三角形的二维图形,而且问题的答案与各层数据间关系复杂,不适合用分治算法分解为与原问题相似的子问题。
下面就学习用动态规划解决此问题。
算法设计:动态规划设计过程如下。
1.阶段划分
从数塔问题的特点来看,不难发现解决问题的阶段划分,应该是自下而上逐层决策。不同于贪婪策略的是做出的不是唯一决策,第一步对于第五层的8个数据,做如下4次决策:
对经过第四层2的路径,在第五层的19,7中选择19;
对经过第四层18的路径,在第五层的7,10中选择10;
对经过第四层9的路径,在第五层的10,4中也选择10;
对经过第四层5的路径,在第五层的4,16中选择16。
这是一次决策过程,也是一次递推过程和降阶过程。因为以上的决策结果将5阶数塔题变为4阶子问题,递推出第四层与第五层的和为:
21(2+19),28(18+10),19(9+10),21(5+16)

用同样的方法还可以将4阶数塔问题变为3阶数塔问题……最后得到的1阶数问题,就是整个问题的最优解。
2、存储、求解
1)原始信息存储
原始信息有层数和数塔中的数据,层数用一个整型变量 n 存储,数塔中的数据用二维数组 data ,存储成如下的下三角阵:

9

12 15

10  6   8

2   18  9  5

19  7  10  4  16

2)动态规划过程存储
由于早期阶段动态规划决策的结果是一组数据,且本次的决策结果是下次决策的唯一依据(无后效性),所以必须在存储每一次决策的结果,若仅仅是求最优解,用一个一维数组存储最新的决策结果即可;但若要同时找出最优解的构成或路径,则必须用二维数组 d 存储各阶段的决策结果。根据上面的算法设计,二维数组 d 的存储内容如下:

 d [ n ][j]= data [ n ][j]    j=1,2,……,n;
 i = n -1, n -2,…,1, j =1,2,…, i 时
 d [ i ][ j ]= max ( d [ i +1][j], d [ i +1][j+1])+ data [ i ][j]
最后 d [1][1]存储的就是问题的结果。

二、代码

#include<stdio.h>
int main()
{
 int a [50][50][3], i , j , n ;
 printf (" please input the number of rows :\n");
 scanf("%d",&n);
 for ( i =1; i <= n ; i = i +1)
   for ( j =1; j <= i ; j = j +1)
    { 
        scanf("%d",&a [ i ][ j ][1]);
        a [ i ][ j ][2]= a [ i ][ j ][1];
        a [ i ][ j ][3]=0;
    }
 for ( i = n -1; i >=1; i = i -1)
   for ( j =1; j <= i ; j = j +1)
    if ( a [ i +1][ j ][2]> a [ i +1][ j +1][2])
        {
        a [ i ][ j ][2]= a [ i ][ j ][2]+ a [ i +1][ j ][2];
        a [ i ][ j ][3]=0;
        }
   else 
       {
       a [ i ][ j ][2]= a [ i ][ j ][2]+ a [ i +1][ j +1][2]; a [ i ][ j ][3]=1;
       }
   printf (" max =%d\n", a [1][1][2]);
    j =1;
for ( i =1; i <= n -1; i = i +1)
{ printf ( "%d->",a [ i ][ j ][1]);
 j = j + a [ i ][ j ][3];
}
 printf ("%d", a [ n ][ i ][1]);
 return 0;
}

三、运行结果 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值