*
第九课
动态规划(I)
综合实践考核
数字三角形
1、问题描述
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的数或者右边的数。
问题描述
输入数据
输入的第一行是一个整数N (1 < N <= 100),给出三角形的行数。下面的N 行给出数字三角形。数字三角形上的数的范围都在0 和100 之间。
输出要求
输出最大的和。
问题描述
输入样例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例
30
2、解题思路
这道题目可以用递归的方法解决。基本思路是:
以D( r, j)表示第r 行第 j 个数字(r,j 都从1 开始算),以MaxSum(r, j) 代表从第 r 行的第 j 个数字到底边的最佳路径的数字之和,则本题是要求 MaxSum(1, 1) 。
从某个D(r, j)出发,显然下一步只能走D(r+1, j)或者D(r+1, j+1)。如果走D(r+1, j),那么得到的MaxSum(r, j)就是MaxSum(r+1, j) + D(r, j);如果走D(r+1, j+1),那么得到的MaxSum(r, j)就是MaxSum(r+1, j+1) + D(r, j)。所以,选择往哪里走,就看MaxSum(r+1, j)和MaxSum(r+1, j+1)哪个更大了。
3、参考程序 I
#include <>
#define MAX_NUM 100
int D[MAX_NUM + 10][MAX_NUM + 10];
int N;
int MaxSum( int r, int j)
{
if( r == N )
return D[r][j];
int nSum1 = MaxSum(r+1, j);
int nSum2 = MaxSum(r+1, j+1);
if( nSum1 > nSum2 )
return nSum1+D[r][j];
return nSum2+D[r][j];
}
3、参考程序 I
int main(void)
{
int m;
scanf("%d", &N);
for( int i = 1; i <= N; i ++ )
for( int j = 1; j <= i; j ++ )
scanf("%d", &D[i][j]);
printf("%d", MaxSum(1, 1));
return 0;
}
提交结果:Time Limit Exceed
程序I分析
上面的程序,效率非常低,在N 值并不大,比如N=100 的时候,就慢得几乎永远算不出结果了。
为什么会这样呢?是因为过多的重复计算。
我们不妨将对MaxSum 函数的一次调用称为一次计算。那么,每次计算MaxSum(r, j)的时候,都要计算一次MaxSum(r+1, j+1),而每次计算MaxSum(r, j+1)的时候,也要计算一次MaxSum(r+1, j+1)。重复计算因此产生。
在题目中给出的例子里,如果我们将MaxSum(r, j)被计算的次数都写在位置(r, j),那么就能得到右面的三角形:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
程序分析
从上图可以看出,最后一行的计算次数总和是16,倒数第二行的计算次数总和是8。不难总结出规律,对于N 行的三角形,总的计算次数是2^0 +2^1+2^2+…+2^(N-1)=2^N-1。当N= 100 时,总的计算次数是一个让人无法接受的大数字。
既然问题出在重复计算,那么解决的办法,当然就是,一个值一旦算出来,就要记住,以后不必重新计算。即第一次算出MaxSum(r, j)的值时,就将该值存放起来,下次再需要计算MaxSum(r, j)时,直接取用存好的值即可,不必再次调用MaxSum 进行函数递归计算了。这样,每个MaxSum(r, j)都只需要计算1 次即可,那么总的计算次数(即调用MaxSum 函数的次数)就是三角形中的数字总数,即1+2+3+…+N = N(N+1)/2。
如何存放计算出来的MaxSum(r, j)值呢?显然,用一个二维数组aMaxSum[N][N]就能解决。aMaxSum[r][j]就存放MaxSum(r, j)的计算结果。下次再需要MaxSum(r, j)的值时,不必再调用MaxSum 函数,只需直接取aMaxSum[r][j]的值即
动态规划基础讲解及经典案例分析解答 来自淘豆网www.taodocs.com转载请标明出处.