C++ 每日一题13:数字金字塔
每日一题13:数字金字塔
题目描述
观察如下数字金字塔。请写一个程序查找从最高点到底部任意点(数字金字塔共有,行)结束的路径,使路径经过数字的和最大,每一步只能走到左下方的点或右下方的点。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在如上样例中,7→3→8→7→5的路径产生了所有路径中最大的和7+3+8+7+5=30。
输入格式
第1行输入,表示行数。
之后每行为数字金字塔特定行包含的整数(所有数非负且不大于100)。
输出格式
一行,路径产生的最大的和。
分析
方法1
直接搜索。设二维数组存放数字金字塔中的每个整数,存放最终结果即最大的和。问题要求从最高点按照规则走到最低点的路径的最大权值和,路径起终点固定,走法规则明确,可考虑使用搜索解决问题。
定义无返回类型递归函数 ,其中,表示当前已从走到,目前已走路径上的权值和为。
当时,到达递归出口,如果,则把更新为。当时,未到达递归出口,则向下一行两个位置行走,即递归执行和。
方法1实际上把所有路径都走了一边,由于每一条路径由步组成,每一步有左右两种选择,因此路径总数为,时间复杂度为,太大会超时。
方法2
方法1之所以会超时,是因为进行了重复搜索。当多次来到同一点时,可以直接调用来到这一点时的路径权值和,避免重复搜索。该方法称为记忆化搜索。
定义 表示从出发到终点的路径最大权值和,所以为所求答案。计算时考虑第一步是向左还是向右,就把所有路径分成两大类。
第一步向左的路径:从出发到终点的这类路径就被分成两个部分,先从到,要使得这种情况的路径权值和最大,第二部分从到终点的路径权值和也要最大,所以这一部分可以表示成。综上所述,第一步向左的路径最大权值和为。
第一步向右的路径:从出发到终点的这类路径就被分成两个部分,先从到,要使得这种情况的路径权值和最大,第二部分从到终点的路径权值和也要最大,所以这一部分可以表示成。综上所述,第一步向左的路径最大权值和为。分析方法与如上分类同理。
同样为了避免重复搜索,开设全局数组记录从每个点出发到终点路径的最大权值和,一开始全部初始化为-1表示未被计算过(即未到达过这一点)。在计算时,首先查询,如果,说明之前已被计算过,直接返回即可,否则计算出的值并存储在中,以便下次调用。
由于对于每个合法的都计算且仅计算过一次,而且是在内完成的,因此时间复杂度为,不超时。
方法3
方法3使用动态规划完成。方法2的记忆化搜索本质上与动态规划高度相似。
确定状态
题目要求从出发到底层路径的最大权值和,路径是由各个点串联而成,路径起点固定,终点和中间点相对不固定。因此定义表示从出发到达的路径最大权值和,并设······。
确定状态转移方程和边界条件
不考虑到的每一步是如何走的,只考虑最后一步是如何走的,根据最后一步是向左还是向右分成如下两种情况:
最后一步向左的路径:最后一步是从到,此类路径被分割成两部分,第一部分是从走到,第二部分是从走到,要计算此类路径的最大权值和,必须用到第一部分的最大权值和,此部分问题的性质与一样,就是,第二部分就是。两部分相加即得到此类路径的最大权值和为。
最后一步向右的路径: 最后一步是从到,此类路径被分割成两部分,第一部分是从走到,第二部分是从走到,要计算此类路径的最大权值和,必须用到第一部分的最大权值和,此部分问题的性质与一样,就是,第二部分就是。两部分相加即得到此类路径的最大权值和为。
的计算需要求出如上两种情况的最大值。综上所述,得到状态转移方程为。
与递归关系式需要终止条件一样,这里也需要对边界进行处理防止无限递归。计算时需要用到和,随着递归的深入,最终都要用到,的计算不可再用状态转移方程,而是应直接赋予一个特值。
所以根据上述得到边界条件为。
现在分析一下此动态规划的正确性,分析该解法是否满足使用动态规划的两个前提:
最优化原理:这个在分析状态转移方程时已经较为透彻,明显符合最优化原理。
无后效性:状态转移方程中,只会关心和的值,计算或时可能有多种不同的决策对应着最优值,选哪种决策对计算的决策没有影响,符合无后效性。
实现
由于状态转移方程就是递归关系式,边界条件就是递归终止条件,所以可以用递归来完成。递归存在重复调用,利用记忆化搜索可以解决这一点。记忆化搜索实现比较简单,且不会计算无用状态,但递归也会受到"栈的大小"和"递推+回归执行方式"的约束,另外记忆化实现调用状态的顺序是按照实际需求展开,没有大局规划,不利于进一步优化。
一种迭代法与分析边界条件相类似,计算用到状态和,这两个元素都在的上一行。也就是说,要计算第行的状态的值,必须先把第行的状态的值计算出来,再利用状态转移方程。可以先把赋值为,再从第2行开始按照行递增的顺序计算出每一行的有效状态。
对于题目所给样例,计算的结果如下表所示:
1 | 2 | 3 | 4 | 5 | |
1 | 7 | / | / | / | / |
2 | 10 | 15 | / | / | / |
3 | 18 | 16 | 15 | / | / |
4 | 20 | 25 | 20 | 19 | / |
5 | 24 | 30 | 27 | 26 | 24 |
该动态规划时间复杂度为,不会超时。
代码
方法1
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005],n,ans;
void dfs(int x,int y,int cur){if(x==n){ans=max(ans,cur);return;}dfs(x+1,y,cur+a[x+1][y]);dfs(x+1,y+1,cur+a[x+1][y+1]);
}
int main(){cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)cin>>a[i][j];dfs(1,1,a[1][1]);cout<<ans;return 0;
}
方法2
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005],f[1005][1005],n;
int dfs(int x,int y){if(f[x][y]==-1)f[x][y]=(x==n?a[x][y]:a[x][y]+max(dfs(x+1,y),dfs(x+1,y+1)));return f[x][y];
}
int main(){memset(f,-1,sizeof(f));cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)cin>>a[i][j];dfs(1,1);cout<<f[1][1];return 0;
}
方法3
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005],f[1005][1005],n,ans;
int main(){cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)cin>>a[i][j];f[1][1]=a[1][1];for(int i=2;i<=n;i++)for(int j=1;j<=i;j++)f[i][j]=a[i][j]+max(f[i-1][j-1],f[i-1][j]);for(int i=1;i<=n;i++)ans=max(ans,f[n][i]);cout<<ans;return 0;
}
C++ 每日一题13:数字金字塔相关推荐
- 【寒假每日一题】数字三角形(个人练习)详细题解+推导证明(第二天)
文章目录 前言 题目 详细题解 写法1 O ( n 2 ) O(n^2) O(n2) 推导证明 写法2 O ( n 2 ) O(n^2) O(n2) 推导证明 举一反三 总结 前言 昨天真是人生中奇葩 ...
- JS 每日一题 #13
这是一个从基础到进阶的JavaScript问题列表,题源来自Github的 JavaScriptQuestions,有32K star. 前端开发博客 打算每日更新一道题,一起来学习打卡吧.如果觉得内 ...
- 5位数的数字黑洞是多少_每日一题[491]数字黑洞--Kaprekar常数
定义$\overline{abc}$是一个三位数,其中各数位上的数字$a,b,c\in \{ 0,1,2,3,4,5,6,7,8,9 \}$且不全相同.定义如下运算$f$:把$\overline{ab ...
- LeetCode每日一题: 缺失数字(No.268)
题目:缺失数字 给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数. 复制代码 示例: 输入: [3,0,1] 输出: 2输入: [9,6 ...
- C语言每日一题——查找数字
在一组"有序"数组中查找某个数字,如果找到返回其下表,如果没有找到,输出"不存在". 一,遍历查找 就是将所有数都遍历一遍 找是否存在该数字如果存在返回下标 具 ...
- C语言每日一题——数字金字塔
如果有同学问你,"你会用代码打印出,数字金字塔吗?" 如果你阅读了这篇文章你将,会十分自信的去告诉他,这都小意思. //数子金字塔 //1//01(0代表空格 方便演示) //12 ...
- 北妈每日一题:JS从无序乱码找我要的数字!
点击上方"前端你别闹",关注并星标 喜欢我的都关注我了 北妈每日一题 我需要整齐排列 问题1:有这样一串杂乱无章的数据: [dahsidoai 213907;a oas198jdo ...
- 电动力学每日一题 2021/10/13 用Fourier变换法计算静止电荷产生的电场
电动力学每日一题 2021/10/13 用Fourier变换法计算静止电荷产生的电场 静止点电荷 具有均匀线密度的静止电荷产生的电场 具有均匀面密度的静止电荷产生的电场 用Fourier变换法计算电场 ...
- 【解题报告】Leecode 423. 从英文中重建数字——Leecode每日一题系列
今天是坚持每日一题打卡的第二十五天 题目链接:https://leetcode-cn.com/problems/reconstruct-original-digits-from-english/ 题解 ...
最新文章
- yii2嵌入微信公众号支付
- golang runtime.findrunnable epoll_wait lock 占用CPU 过多排查
- panel.setLayout(null);
- SimpleDateFormat详解
- 【转】C/C++的64位整型 不同编译器间的比较
- python 结巴分词学习
- wi7计算机桌面删除,如何删除win7系统桌面IE图标|win7删除桌面IE图标的方法
- ECSHOP整合第三方登录,ECSHOP第三方QQ登录插件,ECSHOP第三方新浪微博登录插件,ECSHOP第三方支付宝登插件,ECSHOP淘宝登陆插件,ECSHOP第三方MSN登录
- 软件测试工程师未来十年的职业规划
- 计算机显示找不到gpedit,Win7系统gpedit.msc找不到的解决方法
- Kubernetes pod的生命周期
- GNU C++ 智能指针6-- 解析_Sp_counted_inplace类
- Redis设置过期时间为当月月底-----自动计算
- 计算机台式硬件排名,CPU天梯图2019年1月最新版 一月台式电脑处理器排名
- 亿佰特LoRaWAN入网TTN并订阅MQTT消息
- 可以分屏的软件_mac必备软件
- 南京印象之出租车司机
- 单例模式和多例模式详解
- java的字典序排序_java字典序排序
- 工业机器人 郝卫东_六自由度机器人焊接轨迹研究