题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的样例中,从 7→3→8→7→5 的路径产生了最大
输入格式
第一个行一个正整数 r ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
输出格式
单独的一行,包含那个可能得到的最大的和。
输入输出样例
输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出
30
说明/提示
【数据范围】
对于 100\%100% 的数据,1\le r \le 10001≤r≤1000,所有输入在 [0,100][0,100] 范围内。
题目翻译来自NOCOW。
USACO Training Section 1.5
IOI1994 Day1T1
这题也是经典的动态规划问题
动态规划的四个步骤
第一步确定状态
设一个dp数组表示走到第i行j列时路径之和最大
第二步状态转移方程
再来看看这个样例
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
7 3 8 2 4为行,7 8 0 4 5是列
i表示行 ,j表示列
我们假设走到第三行第二列数字为7的地方我们可以枚举出到达这个地方的所有路径
可以得到一个规律,那就是这个7肯定是由7上面的8和1走过来的,所以我们就可以通过这个确定状态转移方程。
取由从8->7或者1->7之间最大值即可 8的行号和列号是7的行号和列号分别减一也即是
i-1,j-1。1可以表示为i-1,j。所以我们可以写出状态转移方程
a数组是数字三角形a[i][j]表示第i行第j列中的数值
dp[i,j]=max(dp[i-1,j-1]+a[i][j],dp[i-1,j]+a[i][j])
第三步初始化dp数组
因为测试样例中有负数的原因,所以我们要将dp数组初始化为负无穷
也要考虑边界情况如果刚好到达边界的话那只可能从i-1,j-1中到达i,j如果是从i-1,j中到达i,j的话就会越界访问因为i-1,j并没有值所以我们就要在数字三角形中每一行多初始化一个
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= i + 1; j++)
{
dp[i][j] = -99999;
}
}
所有的路径必定经过1,1所有我们手动将1,1输入
dp[1][1] = a[1][1];
第四步确定遍历顺序,这里是从前向后推到的所以我们也是从前往后计算
完整代码
#include<stdio.h>
#define N 1100
int dp[N][N];//表示的是走到当前位置的路径最大
int a[N][N];//数字三角形
int n;//行
int max(int a, int b)//求最大值
{
return a > b ? a : b;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) //数字三角形的输入
{
for (int j = 1; j <= i; j++)
{
scanf("%d", &a[i][j]);
}
}
for (int i = 0; i <= n; i++)//初始化dp数组
{
for (int j = 0; j <= i + 1; j++)
{
dp[i][j] = -99999; //初始化为负无穷
}
}
dp[1][1] = a[1][1]; //手动初始化1,1
for (int i = 2; i <= n; i++) //因为第一行第一列是必定经过的点已经在上面初始化过了所以我们直接从2开始遍历
{
for (int j = 1; j <= i; j++)
{
dp[i][j] = max(dp[i - 1][j - 1] + a[i][j], dp[i - 1][j] + a[i][j]);
}
}
int res = -99999;
for (int i = 1; i <= n; i++) //然后还要找出dp数组中最后一行的最大值就是我们需要的答案
res = max(res, dp[n][i]);
printf("%d", res);
return 0;
}