21-数字三角形(1)

(从这里开始,我们要学点新东西啦,动态规划,仍然边说例子,边说思想,从案例中锻炼算法思想。
例题:数字三角形

题目描述

在这里插入图片描述
在数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。三角形的行数大于1小于等于100,数字为0 - 99

输入格式
5 //三角形行数。下面是三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出
输出最大和

问题分析

用二维数组存放数字三角形。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>
using namespace std;
#define MAX 101
int n;
int a[MAX][MAX];
//MaxSum(i,j)表示从第i行第j个元素到最后一行路径,经过的数字之和最大
int maxsum(int i, int j)
{
	if (i == n)
		return a[i][j];
	int x = maxsum(i + 1, j);
	int y = maxsum(i + 1, j + 1);
	return max(x, y) + a[i][j];
}

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= i; j++)
			cin >> a[i][j];
	cout << maxsum(1, 1)<<endl;
	return 0;
}

这个问题看似解决了,但在提交的时候一定会超时,为什么呢,因为存在大量的重复计算。我们继续向下看。

在这里插入图片描述

如图所示的三角形,黑色的数字表示该位置的元素,红色数字表示该位置的最大路径计算了几次。
要计算从顶元素7开始的最大路径和,要计算左下方以3为顶的最大路径和,右下角以8为顶的最大路径和。在计算以3为顶的最大路径和时,计算了以1为顶的路径,计算以8为顶的最大路径和时,又计算了以1为顶的路径,计算了两次,再向下计算,又出现了更多了重复。

第1行计算次数:1
第2行计算次数:2
第3行计算次数:4
第4行计算次数:8
……
第n行计算次数:2n-1

则总的时间复杂度为2n,对于n = 100 行,肯定超时。

改进方法

如果每算出一个 MaxSum(r,j) 就保存起来,下次用到其值的时候直接取用,则可免去重复计算。那么可以用O(n2)时间完成计算。因为三角形的数字总数是n(n+1)/2

#include <iostream>
#include <algorithm>
using namespace std;

#define MAX 101
int D[MAX][MAX];
int maxsum[MAX][MAX];
int n;
//MaxSum(i,j)表示从第i行第j个元素到最后一行路径,经过的数字之和最大
int MaxSum(int i, int j)
{
//如果maxsum[i][j]为-1,表示从该节点出发的路径还没算过
//如果不是-1,已经算过了,直接取用就行了
	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()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= i; j++)
		{
			cin >> D[i][j];
			maxsum[i][j] = -1;
		}
	cout << MaxSum(1, 1) << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值