一、 实践题目
给定一个由 n行数字组成的数字三角形如下图所示。试设计一个算法,计算出从三角形 的顶至底的一条路径(每一步可沿左斜线向下或右斜线向下),使该路径经过的数字总和最大。
输入格式:
输入有n+1行:
第 1 行是数字三角形的行数 n,1<=n<=100。
接下来 n行是数字三角形各行中的数字。所有数字在0..99 之间。
输出格式:
输出最大路径的值。
输入样例:
在这里给出一组输入。例如:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
在这里给出相应的输出。例如:
30
二、 问题描述
从题目看,从三角形 的顶至底的一条路径(每一步可沿左斜线向下或右斜线向下),使该路径经过的数字总和最大,意思就是找顶点到底的一条路径,该路径数字总和最大,即求最优解的问题。
分析: 以3为的顶点的三角形最优解依赖以3和8为顶点的两个三角形中最优解更大的三角形,这样所求解一定满足要求。而以3为顶点的三角形又依赖以8和3为顶点的两个三角形中最优解更大的三角形。一直递推到三角形底部,从一个问题的解决需要依赖它的子问题的解,子问题的解又依赖子子问题的解的特点来看,这是一个动态规划问题。
求以第(i,j)个数为顶点的三角形的最优路径时,我们需要对比以它左下方数为顶点的三角形的最优解和以它右下方数为顶点的三角形的最优解,比较大的那个最优解加上第(i,j)个数的值就是这个问题的解。
三、 算法描述
根据问题分析我们可以得到递推方程式为 // R为名的2维数组存放三角形,MaxSum数组存放以各位置为顶点三角形最优解,MaxSum[1][1]就是这个问题最终的解。
MaxSum[i][j] = R[i][j]; if n = I; //行数为最后一行,即为底时。
= Max(MaxSum[i+1][j],MaxSum[i+1][j+1])+R[i][j]
由公式我们也能知道填表方式为从下到上,从左到右
for(int i=1; i<=n; i++)
{
MaxSum[n][i] = a[n][i];
}
for(int i=n-1; i>=1; i--)
{
for(int j = 1; j<=n; j++)
{
MaxSum[i][j] = max(MaxSum[i+1][j], MaxSum[i+1][j+1] ) + a[i][j];
//max函数筛选出2个数中较大的数
}
}
cout << MaxSum[1][1] << endl;
四、 算法时间及空间复杂度分析
算法时间重点在于填表的两重循环。
for(int i=n-1; i>=1; i--) //外层 n-1次
{
for(int j = 1; j<=n; j++) //内层 n次
{
MaxSum[i][j] = max(MaxSum[i+1][j], MaxSum[i+1][j+1] ) + a[i][j]; }
总次数为 (n-1)*n=n2-n 所以时间复杂度为O(n2)
空间复杂度
用了MaxSum[n][n]的一个二维数组,空间复杂度为O(n2)。
五、 心得体会
通过这个实验我对动态规划的理解更加清晰,之前听课只知道它跟分治法很类似,都是将一个问题拆分成多个子问题解决然后将多个子问题解结合就得到原问题的解,也知道每个子问题间的依赖关系。但是一开始下手的时候明明知道题目怎么做就是想不到代码怎么敲,填表也不知道怎么填,但是后面知道要写递推式就好解决问题。于是就写出了递推式然后问题就迎刃而解了。之后敲代码就比较好敲了,用一个数组存三角形,一个数组存最优解。可以知道递推式在动态规划问题是很重要的,可以说有了递推式就有了表,有了表就等于解决了问题。