观察下面的数字金字塔。写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以从当前点走到左下方的点也可以到达右下方的点。
在上面的样例中,从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
算法分析:
如果穷举所有路径,然后记录路径上的数字之和的最大值,将会产生2^n的复杂度。令dp[i][j]表示从第i行第j列个数字开始到最底层的最大和,在定义这个数组之后dp[1][1]就是我们想要求的答案。
状态:dp[i][j]
状态转移方程:dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j]
边界:dp[n][j]=f[n][j]
这里二维数组f记录的是第i行第j列存储的数字。
递推参考代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int f[1005][1005];
int dp[1005][1005];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
scanf("%d",&f[i][j]);
// dp[i][j]=-1;
}
for(int i=1;i<=n;i++)
dp[n][i]=f[n][i];
for(int i=n-1;i>=1;i--)//逆推
for(int j=1;j<=n;j++)
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j];
printf("%d",dp[1][1]);
return 0;
}
数字三角形递归写法:
参考代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
int f[1005][1005];
int dp[1005][1005];
int maxd(int i,int j)
{
if(dp[i][j]!=-1) return dp[i][j];
if(i==n) dp[i][j]=f[i][j];
else{
int x=maxd(i+1,j);
int y=maxd(i+1,j+1);
dp[i][j]=max(x,y)+f[i][j];
}
return dp[i][j];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
scanf("%d",&f[i][j]);
dp[i][j]=-1;
}
printf("%d",maxd(1,1));
return 0;
}
递推写法至下而上,从边界开始不断向上解决问题,知道解决了目标问题。递归写法自上而下,从目标问题开始,将它分解成子问题的组合,直到分解至边界为止。
通过上面的栗子再引申出一个概念**:如果一个问题的最优解可以由其子问题的最优解有效地构造出来,那么称这个问题拥有最优子结构**。最优子结构保证了动态规划中原问题的最优解可以由子问题的最优解推导出来,因此,一个问题必须拥有最优子结构,才能使用动态规划去解决。
一个问题必须拥有重叠子问题和最优子结构,才能使用动态规划去解决。