动态规划——数字三角形

问题描述

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

#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

#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;
}
示意图
动态规划——数字三角形_第1张图片
空间优化
  • 没必要使用二维maxSum数组存储每一个MaxSum(r,j),只要从底层一行向上递推,那么只要一维数组maxSum[100]即可,即只要存储一行的MaxSum值就可以。
  • 进一步考虑,连maxSum数组都可以不要,直接用D的第n行替代maxSum即可。
  • 节省空间,时间复杂度不变。
改进代码如下:
#include

#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——《程序设计与算法》(北京大学 郭炜)

  • 算法系列目录:
    • 一、递归形式的问题
      • 汉诺塔问题(Hanoi)
      • N皇后问题
      • 逆波兰表达式
      • 四则运算表达式求值
      • 爬楼梯
      • 放苹果
      • 算24
    • 二、程序或算法的时间复杂度
    • 三、二分算法
      • 二分查找
      • 二分法求方程的根
      • 寻找指定和的整数对
    • 四、分治算法
      • 归并排序、快速排序
      • 输出前m大的数
      • 求排列的逆序数
      • 求最大子数组
    • 五、动态规划
      • 数字三角形

你可能感兴趣的:(算法设计与分析)