动态规划——数字三角形

问题描述

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

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

#define MAX 101
int D[MAX][MAX];
int n;

int max(int a, int b)
{
	if(a-b >= 0)
		return a;
	else
		return b;
}

int MaxSum(int i, int j)
{
	int x, y;
	if(i == n)
		return D[i][j];
	x = MaxSum(i+1, j);
	y = MaxSum(i+1, j+1);
	return max(x, y) + D[i][j];
}


int main()
{
  int i, j;
  scanf("%d", &n);
  for(i = 1; i <= n; i++)
    for(j = 1; j <= i; j++)
		scanf("%d", &D[i][j]);
  printf("The MaxSum is %d\n", MaxSum(1, 1));
  return 0;
}
  • 上述代码采用递归的方法,深度遍历每一条路径,存在大量重复计算。则时间复杂度为 2 n 2^n 2n,对于n = 100行,肯定超时。
改进
  • 如果每次算出一个MaxSum(r,j)就保存起来,下次用到其值的时候直接取用,则可以免去重复计算。那么可以用 O ( n 2 ) \Omicron(n^2) O(n2)时间完成计算。因为三角形的数字总数时n(n+1)/2
  • 改进代码如下:
#include<stdio.h>

#define MAX 101
int D[MAX][MAX];
int maxSum[MAX][MAX];
int n;

int max(int a, int b)
{
	if(a-b >= 0)
		return a;
	else
		return b;
}

int MaxSum(int i, int j)
{
	int x, y;
	if(maxSum[i][j] != -1)
		return maxSum[i][j];
	if(i == n)
		maxSum[i][j] = D[i][j];
	x = MaxSum(i+1, j);
	y = MaxSum(i+1, j+1);
	maxSum[i][j] = max(x, y) + D[i][j];
	return maxSum[i][j];
}


int main()
{
  int i, j;
  scanf("%d", &n);
  for(i = 1; i <= n; i++)
    for(j = 1; j <= i; j++)
	{
		scanf("%d", &D[i][j]);
		maxSum[i][j] = -1;
	}
		
  
  printf("The MaxSum is %d\n", MaxSum(1, 1));
  return 0;
}
示意图
空间优化
  • 没必要使用二维maxSum数组存储每一个MaxSum(r,j),只要从底层一行向上递推,那么只要一维数组maxSum[100]即可,即只要存储一行的MaxSum值就可以。
  • 进一步考虑,连maxSum数组都可以不要,直接用D的第n行替代maxSum即可。
  • 节省空间,时间复杂度不变。
改进代码如下:
#include<stdio.h>

#define MAX 101
int D[MAX][MAX];
int *maxSum;
int n;

int max(int a, int b)
{
	if(a-b >= 0)
		return a;
	else
		return b;
}

int main()
{
	int i, j;
	scanf("%d", &n);
	for(i = 1; i <= n; i++)
		for(j = 1; j <= i; j++)
			scanf("%d", &D[i][j]);
	maxSum = D[n];
	for(i = n-1; i >= 1; i--)
		for(j = 1; j <= i; j++)
				maxSum[j] = max(maxSum[j], maxSum[j+1]) + D[i][j];
  printf("The MaxSum is %d\n", maxSum[1]);
  return 0;
}

注:文中问题及代码参考 MOOC——《程序设计与算法》(北京大学 郭炜)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值