信息学奥赛一本通 1258:【例9.2】数字金字塔

【题目链接】

ybt 1258:【例9.2】数字金字塔

【题目考点】

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;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值