动态规划---数字三角形

题目描述:

在数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大,路径上的每一步都只能往左下或右下走,只需要求出这个最大和即可,不必给出具体路径
在这里插入图片描述
三角形的行数大于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;
}

这就是算法优化和空间优化的最终结果

发布了92 篇原创文章 · 获赞 35 · 访问量 6372

猜你喜欢

转载自blog.csdn.net/CourserLi/article/details/103638024