算法第三章实践报告

1.实践题目:数字三角形 

 

2.问题描述:

  给定一个由 n行数字组成的数字三角形如下图所示。试设计一个算法,计算出从三角形 的顶至底的一条路径(每一步可沿左斜线向下或右斜线向下),使该路径经过的数字总和最大。

 

3.算法描述:

  首先,将这个数字三角形存储在一个二维数组num[n+1][n+1]中,且采用左下角的直角三角形的方式存储(第0行和第0列储存为0,方便操作)。例如,将PTA上的样例输入存储为如下所示:

  0  0  0  0  0  0

  0  7  0  0  0  0
  0  3  8  0  0  0
  0  8  1  0  0  0
  0  2  7  4  4  0
  0  4  5  2  6  5

  然后,第1行第1列(7)开始从上到下遍历,并将它置为它上方的数及左上方的数的较大值与它自身的和:num[i][j] = num[i][j] + max(num[i-1][j-1], num[i-1][j]);

  遍历完成后,最后一行的数都是它上面的数的较大值的总和,以PTA的样例输入为例,经过上述操作以后该数组变成:

  0  0   0   0   0   0

  0  7   0   0   0   0 
  0 10 15  0   0   0 
  0 18 16 15  0   0 
  0 20 25 20 19  0 
  0 24 30 27 26 24

  因此所求的最大路径就是最后一行的最大值。这里使用简单求数组最大值的方法找到了最大值为30:

  int max = 0;
  for (int i = 1; i <= n; i++)
    if (num[n][i] > max)
      max = num[n][i];

3.算法时间及空间复杂度分析:

  时间复杂度:该算法在遍历阶段采用了双层for循环进行遍历,因此时间复杂度为O(n^2)。

  空间复杂度:该算法是直接在原数组上操作的,因此辅助空间较少,空间复杂度为O(1)。

 

4.心得体会(对本次实践收获及疑惑进行总结)

  这个题目的主要难点在于如何保存数组以及写出递推公式。由这个数字三角形的形状我第一反应是用二叉树来保存,然后使用中序遍历或前序遍历来找到最大路径。但好像没有预设的二叉树的数据结构,而且操作也有诸多困难,因此我最后选择了二维数组。这里我用行列长度比原长度大1的方式,空出第0行和第0列,主要是为了递归时的方便,不用考虑边界的问题。好在这道题的递推公式也不难写,因此我按自己对动态规划的印象写出了num[i][j] = num[i][j] + max(num[i-1][j-1], num[i-1][j])。这道题有一个坑是我们往往第一反应是“每次分叉都选最大值,那么结果不就是最大路径了吗?”但这其实是犯了一个常见的错误:认为局部最优解就是整体最优解。例如,样例输入中,我们每次分叉选择较大值,即7+8+1+7+5=28。但实际上最优解是3+7+8+7+5=30。因此,我们在解决类似问题的时候,一定要注意这个细节,即局部最优解并不一定等于整体最优解。

posted @ 2018-11-07 21:38  wanderlust  阅读(128)  评论(0编辑  收藏  举报