题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的样例中,从7→3→8→7→5 的路径产生了最大
输入格式
第一个行一个正整数 r ,表示行的数目。后面每行为这个数字金字塔特定行包含的整数。
输出格式
单独的一行,包含那个可能得到的最大的和。
输入输出样例
输入 #1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出 #1
30
说明/提示
【数据范围】
对于100% 的数据,1≤r≤1000,所有输入在 [0,100]范围内。
题目翻译来自NOCOW。
USACO Training Section 1.5
IOI1994 Day1T1
思路分析
因为从最高点到底部的最大和,可以等于max(左边的点到底部的最大和,右边的点到底部的最大和)加上该数字本身。即原问题可以化为同样形式且规模更小的问题(该问题具有最优子结构性质)。而且底部那一层到底部的最大和等于该数本身(因为该层无法再向下走),所以就解决了动态规划的初始值问题。
递推式
maxsum[i][j] =num[i][j]; i=r
maxsum[i][j] = max(maxsum[i+1][j],maxsum[i+1][j+1]) + num[i][j]; i<r
注意点
- 为了防止数组越界存储数字的数组可以开大一点点。
代码
#include <iostream>
using namespace std;
int maxsum[1005][1005];//maxsum[i][j]代表第i行第j列的数到底部任意处结束经过数字的最大和
int main()
{
int r;
cin>>r;
int num[r+5][r+5];//注意要留出多一些空间
for(int i=1;i<=r;i++)
for(int j=1;j<=i;j++)
cin>>num[i][j];
for(int j=1;j<=r;j++)
maxsum[r][j] = num[r][j];//底部那一层数字到底部的最大和等于其本身
for(int i=r-1;i>0;i--){
for(int j=1;j<=i;j++){
//maxsum[i][j]到达到底部任意处结束经过数字的最大和等于max(左边的点到底部的最大和,右边的点到底部的最大和)加上该数字本身
maxsum[i][j] = max(maxsum[i+1][j],maxsum[i+1][j+1]) + num[i][j];
}
}
cout<<maxsum[1][1]<<endl;//输出最高点到底部的最大和
return 0;
}
扩展知识点
- 一定要用二维数组来存最大和吗?
想明白了吗?小靓仔(女)!!!
答案是不需要的。可以用一维数组来存最大和,因为在求maxsum[i][j]时,maxsum[i+1][j]用过之后就不会在使用了,所以可以将这个值覆盖,综上所述可以用一维数组来存储最大和。
代码
#include <iostream>
using namespace std;
int maxsum[1005];
int main()
{
int r;
cin>>r;
int num[r+5][r+5];//注意要留出多一些空间
for(int i=1;i<=r;i++)
for(int j=1;j<=i;j++)
cin>>num[i][j];
for(int j=1;j<=r;j++)
maxsum[j] = num[r][j];
for(int i=r-1;i>0;i--){
for(int j=1;j<=i;j++){
maxsum[j] = max(maxsum[j],maxsum[j+1]) + num[i][j];
}
}
cout<<maxsum[1]<<endl;//输出最高点到底部的最大和
return 0;
}
- 一定要另外开辟空间来存储最大和?
想明白了吗?小靓仔(女)!!!
答案是不需要另外开辟空间的。可以用存储数字的数组的最后一行来存最大和,因为在求maxsum[i][j]时,num[i][j]用过之后就不会在使用了,所以可以将这个值覆盖,综上所述可以用存储数字的数组的最后一行来存最大和。
代码
#include <iostream>
using namespace std;
int main()
{
int r;
cin>>r;
int num[r+5][r+5];//注意要留出多一些空间
for(int i=1;i<=r;i++)
for(int j=1;j<=i;j++)
cin>>num[i][j];
for(int i=r-1;i>0;i--){
for(int j=1;j<=i;j++){
num[r][j] = max(num[r][j],num[r][j+1]) + num[i][j];
}
}
cout<<num[r][1]<<endl;//输出最高点到底部的最大和
return 0;
}