C++信息学奥赛一本通1258题解

1258:【例9.2】数字金字塔

时间限制: 1000 ms 内存限制: 65536 KB

提交数: 32578 通过数: 19357

【题目描述】

观察下面的数字金字塔。写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以从当前点走到左下方的点也可以到达右下方的点。

在上面的样例中,从138261524的路径产生了最大的和86

【输入】

第一个行包含R(1≤R≤1000),表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

所有的被供应的整数是非负的且不大于100

【输出】

单独的一行,包含那个可能得到的最大的和。

【输入样例】

5
13
11 8
12 7  26
6  14 15 8
12 7  13 24 11

【输出样例】

86

题解

本题作为动态规划(DP)的入门题,主要来了解一下动态规划的思想。我们从题目中截取出这张图:

不难看出,在这样一种倒金字塔,或者说类树的结构中,出题者在引导我们用一种逆序的思想。

怎么逆序呢?具体来说,从倒数第二层开始,由于向下走只有一层,要取最大值,我们肯定取可以到达的两个点中较大值作为目的地,此时对于每个倒数第二层的点来说,从它到终点(最后一层)的最佳策略已经出现了,我们可以把选定的这个较大值累加到对应的倒数第二层的点上去,此时最后一层的点就没用了,就像消消乐一样,最后一层被消掉了,那我们就可以把原来的倒数第二层当作新的最后一层,继续操作,直到只剩下最后一层,此时金字塔顶端的值就是最长路径了。

以上一段是逆序思想的重点,细读几遍,便会发现,这其中已经包含了动态规划的三要素:阶段、状态、决策,其中阶段在本题中指的就是每一层为一个阶段,一层一层递进,状态,即当前最后一层的各个待选项,决策,在这里就是在两个中选择较大的累加。这里的决策,是DP的核心,写成代码就是(数字金字塔用dp数组表示):

dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);

而这一句话,就是大名鼎鼎的状态转移方程!当然,真正题目中的状态转移方程不可能都这么简单,但只要你想出了在两个阶段间转移状态的方法,那这道DP题大概就迎刃而解了。

有了它,代码就简单了,再次注意:逆序,逆序,逆序!

(当然有的同学一定要用正序也可以,不过通常正序的思路会非常难想,老师讲的也大都是逆序,我是我们年段写正序写的最好的,让我写正序,我也是先写逆序,在倒过来就是正序了,哦还有别忘了,以后大概率dp数组和保存数据的数组是分开的)

两段代码都放:

逆序:

#include<bits/stdc++.h>
using namespace std;
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",&dp[i][j]);
    for(int i=n-1; i>=1; i--) //逆序
        for(int j=1; j<=i; j++)
            dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]); //状态转移方程
    printf("%d",dp[1][1]); //输出即起点
    return 0;
}

正序:(不推荐)

#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005],ans;
int main(){
    int n; scanf("%d",&n);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=i; j++)
            scanf("%d",&dp[i][j]);
    for(int i=2; i<=n; i++)
        for(int j=1; j<=i; j++)
            dp[i][j]+=max(dp[i-1][j-1],dp[i-1][j]); //和逆序相反的状态转移方程,可以自己思考一下
    for(int i=1; i<=n; i++) ans=max(ans,dp[n][i]); //正序的最大值保存在最后一行,一个个比较
    printf("%d",ans);
    return 0;
}

同样地,众所周知,这类(简单的,难的没试过)动态规划问题也可以用记忆化搜索解但搜索毕竟是递归,好理解一些,但栈调用的内存大,所以还是慢慢的都改用DP吧。

码字不易,关注点赞收藏支持一下吧!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值