【题目链接】
【题目考点】
1. 记忆化搜索
2. 动态规划基本型
【解题思路】
思路1:一般深搜(非正确解)
每到一个位置,更新加和,向左下,右下两个方向搜索。
搜索到最后一行,判断当前加和是否大于最大加和,更新最大加和。
该搜索过程实际上对很多问题重复计算了很多次,因而时间复杂度较高。只能解决小规模的问题,如果提交该代码的话,必定超时!
#include<bits/stdc++.h>
using namespace std;
#define N 1005
int a[N][N], r;
int maxSum, sum;//最大加和
//从第i,j位置开始搜索
void dfs(int i, int j)
{
sum += a[i][j];
if(i == r)//i==r时,是最下面一行
{
if(sum > maxSum)//更新最大加和
maxSum = sum;
}
else//只要不是最后一行。
{//i,j左下的位置是:i+1,j,右下的位置是:i+1,j+1
dfs(i + 1, j);
dfs(i + 1, j + 1);
}
sum -= a[i][j];
}
int main()
{
cin>>r;
for(int i = 1; i <= r; ++i)
{
for(int j = 1; j <= i; ++j)
cin>>a[i][j];
}
dfs(1, 1);
cout<<maxSum;
return 0;
}
思路2:记忆化递归
思路3:顺推法
思路4:逆推法
【题解代码】
解法1:记忆化递归
设二维数组sumMx,sumMx[i][j]记录i,j位置到底层的最大加和。
- 递归问题为:求第i,j位置到底层的最大加和。
- 递归函数中:
- 如果在sumMx中已经记录了i,j位置到底层的最大加和,则直接返回该值。
- 如果没有记录,那么最大加和为:“该位置左下方位置到底层的最大加和”,和“该位置右下方位置到底层的最大加和”中的最大值,加上该位置的值。而后记录该值。
i,j的左下方位置:i+1,j,右下方位置:i+1,j+1,所以该表达式为:
sumMx[i][j] = a[i][j] + max(dfs(i+1, j), dfs(i+1, j+1));
- 递归退出条件:到达底层
#include<bits/stdc++.h>
using namespace std;
#define N 1005
int a[N][N], r;//a:原始数据,r:行数
int sumMx[N][N];//sumMx[i][j]:记录i,j位置到底层的最大加和
int maxSum, sum;//maxSum:最大加和,sum:临时加和
//求第i,j位置到底层的最大加和
int dfs(int i, int j)
{
if(sumMx[i][j] == -1)//如果这里没有记录
{
if(i == r)//如果已经到底层
sumMx[i][j] = a[i][j];//从该点到底层的最大加和就是该位置的值
else
sumMx[i][j] = a[i][j] + max(dfs(i+1, j), dfs(i+1, j+1));
}
return sumMx[i][j];
}
int main()
{
cin>>r;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= i; ++j)
cin>>a[i][j];
memset(sumMx, -1, sizeof(sumMx));//将sumMx数组初值设为-1,表示这里没有记录数据
cout<<dfs(1, 1);
return 0;
}
解法2:顺推法
- 设二维数组sumMx,sumMx[i][j]记录从1,1位置到i,j位置的最大加和
- 1,1到i,j位置的加和,为1,1到i,j位置左上右上两个位置的加和的最大值,加上i,j位置的值。
i,j左上位置为i-1,j-1;右上位置为i-1,j
所以有:sumMx[i][j] = max(sumMx[i-1][j-1], sumMx[i-1][j]) + a[i][j];
初始情况sumMx[1][1] = a[1][1];
以此可以推出从1,1到底层各位置的最大加和,取其中最大值即可。
#include<bits/stdc++.h>
using namespace std;
#define N 1005
int a[N][N], r;//a:原始数据,r:行数
int sumMx[N][N];//sumMx[i][j]:记录从1,1到i,j位置的最大加和
int mxVal;//最大加和
int main()
{
cin>>r;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= i; ++j)
cin>>a[i][j];
sumMx[1][1] = a[1][1];
for(int i = 2; i <= r; ++i)
for(int j = 1; j <= i; ++j)
{
sumMx[i][j] = max(sumMx[i-1][j-1], sumMx[i-1][j]) + a[i][j];
if(i == r)//求第r行的最大值
{
if(sumMx[i][j] > mxVal)
mxVal = sumMx[i][j];
}
}
cout<<mxVal;
return 0;
}
解法3:逆推法
设数组sumMx[i][j]表示从底层某位置到i,j的最大加和。
从底层向上层递推。
#include<bits/stdc++.h>
using namespace std;
#define N 1005
int a[N][N], r;//a:原始数据,r:行数
int sumMx[N][N];//sumMx[i][j]:记录从底层到i,j位置的最大加和
int mxVal;//最大加和
int main()
{
cin>>r;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= i; ++j)
cin>>a[i][j];
for(int i = 1; i <= r; ++i)//底层到底层的和,即为该位置的值
sumMx[r][i] = a[r][i];
for(int i = r - 1; i >= 1; --i)//从倒数第二行遍历到第1行
for(int j = 1; j <= i; ++j)
sumMx[i][j] = max(sumMx[i+1][j], sumMx[i+1][j+1]) + a[i][j];//i,j位置的加和为其下面两个位置的加和中的最大值加上本位置的值
cout<<sumMx[1][1];//底层某位置到1,1的最大加和,即为要求的值
return 0;
}