算法第三章实践报告
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。因此,我们在解决类似问题的时候,一定要注意这个细节,即局部最优解并不一定等于整体最优解。