题目描述:
在数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大,路径上的每一步都只能往左下或右下走,只需要求出这个最大和即可,不必给出具体路径
三角形的行数大于1小于等于100,数字为 0 - 99
解题思路:
用二维数组存放数字三角形
D( r , j )
:第 r 行第 j 个数字( r 、j 从1开始算)
MaxSum( r , j )
: 从D( r , j )到底边的各条路径中,最佳路径的数字之和
问题:求 MaxSum( 1 ,1 )(典型的递归问题)
从 D( r ,j )出发,下一步只能走D( r + 1 ,j )或者D( r + 1 ,j + 1 ),故对于N行的三角形:
if ( r == N) //到底部时
MaxSum(r,j) = D(r,j)
else
MaxSum( r, j) = Max( MaxSum(r+1,j), MaxSum(r+1,j+1) )+ D(r,j)
#include <iostream>
#include <algorithm>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int MaxSum(int i, int j)
{
if(i==n)
return D[i][j];
int x = MaxSum(i+1,j);
int y = MaxSum(i+1,j+1);
return max(x,y)+D[i][j];
}
int main()
{
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++) cin >> D[i][j];
cout << MaxSum(1,1) << endl;
return 0;
}
但这么算会超时!
每行的次数加起来为( 20+21+22+23+24+…299 == 2100),一般循环超过224次就会超时
改进
如果每算出一个MaxSum(r,j)
就保存起来,下次用到其值的时候直接取用,则可免去重复计算,那么
可以用O(n2)时间完成计算,因为三角形的数字总数(计算次数)是 n(n+1)/2 次(对于复杂度可简写为 n2)
这就引出了 记忆递归型动归 程序:
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX]; int n;
int maxSum[MAX][MAX];
int MaxSum(int i, int j)
{
if(maxSum[i][j]!=-1) return maxSum[i][j];
if(i==n) maxSum[i][j] = D[i][j];
else
{
int x = MaxSum(i+1,j);
int y = MaxSum(i+1,j+1);
maxSum[i][j] = max(x,y)+D[i][j];
}
return maxSum[i][j];
}
int main()
{
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
{
cin >> D[i][j];
maxSum[i][j] = -1;
}
cout << MaxSum(1,1) << endl;
return 0;
}
再改进
众所周知,递归是很浪费时间的,故我们试着寻找更为简单的方法
我们从底层开始算起,将底层的值放入MaxSum(r,j)
中
将 底层的数值 与 倒数第二层的数值 相加 的最优解放入 倒数第二层MaxSum(r,j)
中,换句话说就是 底层两个数值 决定 倒数第二层一个数值,我们再以此类推…
最后 相加到 最顶层 时我们便得到最优解
这就是递推型动规:
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX]; int n;
int maxSum[MAX][MAX];
int main()
{
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++) cin >> D[i][j];
for( int i = 1;i <= n; ++ i ) maxSum[n][i] = D[n][i];
for( int i = n-1; i>= 1; --i ) //从底层开始
for( int j = 1; j <= i; ++j )
maxSum[i][j] = max(maxSum[i+1][j],maxSum[i+1][j+1]) + D[i][j];
cout << maxSum[1][1] << endl;
return 0;
}
既然算法已经优化了,那到空间优化了
没必要用二维 maxSum数组 存储每一个MaxSum(r,j)
,只要从底层一行行向上递推,那么只要一维数组 maxSum[100] 即可,即只要存储一行的 MaxSum值 就可以了
以此类推…
最后 一维数组 的 第一个元素 即结果(最大值)
再优化
进一步考虑,连 maxSum数组 都可以不要,直接用 D数组 的第 n 行替代 maxSum数组 即可,那么我们可以考虑用指针,节省空间,时间复杂度不变
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int n; int* maxSum; //指针
int main()
{
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++) cin >> D[i][j];
maxSum = D[n]; //maxSum指向第 n行
for( int i = n-1; i>= 1; --i )
for( int j = 1; j <= i; ++j )
maxSum[j] = max(maxSum[j],maxSum[j+1]) + D[i][j];
cout << maxSum[1] << endl;
return 0;
}