【例9.2】数字金字塔

【题目描述】

观察下面的数字金字塔。写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以从当前点走到左下方的点也可以到达右下方的点。

在上面的样例中,从13到8到26到15到24的路径产生了最大的和86。

【输入】

第一个行包含R(1≤ R≤1000),表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

所有的被供应的整数是非负的且不大于100。

【输出】

单独的一行,包含那个可能得到的最大的和。

【输入样例】

5

13

11 8

12 7  26

6  14 15 8

12 7  13 24 11

【输出样例】

86

【来源】

​No​

 

题目分析:

动态规划最经典的例题。

正推法:

1.    确定状态:

题目要求从(1,1)出发到最底层路径权值最大,所以定义发f[x][y]是从

(1,1)到(x,y)的路径最大权值和,用f[n][j](n为固定,最后一行,j表示路径数)表示每一条路线权值和。

2.    确定状态转移方程和边界条件:

无论(1,1)到终点(x,y)如何走,只考虑最后一步怎么走。最后一步则被分为向左还是向右两种情况。

~向左:最后一步则为从(x-1,y)到(x,y),第一部分是从(0,0)走到(x-1,y),第二部分(x-1,y)走到(x,y),则这两部分最大权值和为发f[x][y]=f[x-1][y]+a[x][y]。

~向右:最后一步则为从(x-1,y-1)到(x,y),f[x][y]=f[x-1][y-1]+a[x][y]。

状态转移方程:f[x][y]= max( f[x-1][y]  + f[x-1][y-1] )+  a[x][y]

正如递归条件一样,这里我们需要对边境进行处理,以防止无限循环下去,我们发现发计算f[x][y]需要f[x-1][y],f[x-1][y-1,这两又是由上一层得来的,最终肯定会用到发f[1][1],

F[1][1]不能用方程来求,直接赋值a[1][1].

边界条件:f[1][1]=a[1][1];

3.    程序实现:

迭达法:计算f[x][y]用到状态f[x-1][y]和f[x-1][y-1],即计算第x行数值需要用到上一行状态值,因此我们先把第一行f[1][1]=a[1][1],再从第二行开始计算每一行的有效状态即可,时间复杂度为O(n的平方)

4.    代码实现:


#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int a[maxn][maxn],i,j,f[maxn][maxn]; //f记录权值和
int main()
{

memset(f,0,sizeof(f)); //先将f全部赋值为0
int n; //n行
cin>>n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
cin>>a[i][j]; //输出各个节点数字
f[1][1]=a[1][1]; //对f初始化,相当于边界条件
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];
int maxx=0;
for(i=1;i<=n;i++) //计算最大权值和
maxx=max(maxx,f[n][i]);
cout<<maxx<<endl;
return 0;
}