寒假每日一题数字三角形(个人练习)详细题解+推导证明(第二天)
Posted 我是管小亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了寒假每日一题数字三角形(个人练习)详细题解+推导证明(第二天)相关的知识,希望对你有一定的参考价值。
文章目录
前言
昨天真是人生中奇葩的一天,结果没有更博客,是我的锅,今天还是写一个寒假每日一题来坚持坚持,不是软广,不是硬广,只是个人练习,题目来自usaco training 1.6,模板题。
另外,最近发现自己的代码规范写的不是太好,比如for循环。
我以前的写法:
for(int i=0;i<n;++i)
......
看到的好的写法:
for (int i = 0; i < n; ++ i )
......
感觉有必要改进改进代码规范了,华为这两年一直在搞代码可信度,慢慢加油吧。
题目
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输入格式
- 第一行包含整数n,表示数字三角形的层数。
- 接下来n行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。
输出格式
- 输出一个整数,表示最大的路径数字和。
数据范围
- 1 ≤ n ≤ 500 1≤n≤500 1≤n≤500
- − 10000 ≤ 三 角 形 中 的 整 数 ≤ 10000 −10000≤三角形中的整数≤10000 −10000≤三角形中的整数≤10000
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30
详细题解
写法1 O ( n 2 ) O(n^2) O(n2)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
int n;
int w[N][N], f[N][N];
int main()
cin >> n;
for (int i = 1; i <= n; ++ i )
for (int j = 1; j <= i; ++ j )
cin >> w[i][j];
for (int j = 1; j <= n; ++j) f[n][j] = w[n][j];
for (int i = n - 1; i >= 0; -- i )
for (int j = 1; j <= i; ++ j )
f[i][j] = max(f[i+1][j] + w[i][j], f[i+1][j+1] + w[i][j]);
cout << f[1][1] << endl;
return 0;
毫无疑问,这是一道经典的dp问题,每一次三角形中的一个数都是由它的两个角标数相连,即左下标和右下标,即无法记忆前面的状态,只能推导后面的状态,这是很难的。所以如果是从下向上的话,容易太多了。。。不过这可能比较难想,所以dp题目需要经验,当然我的经验也不足,所以还需要锤炼。
根据状态表示和状态计算即可完成推导公式:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
+
1
]
[
j
]
+
w
[
i
]
[
j
]
,
f
[
i
+
1
]
[
j
+
1
]
+
w
[
i
]
[
j
]
)
;
f[i][j] = max(f[i+1][j] + w[i][j], f[i+1][j+1] + w[i][j]);
f[i][j]=max(f[i+1][j]+w[i][j],f[i+1][j+1]+w[i][j]);
最后提交,AC😁
推导证明
本题的推导即为dp问题的推导,按照y总的dp思考法,可以分为几个步骤。
第一个是状态表示:你的集合即数组表示的含义是什么,这个很重要且不能忘记;你的属性即求什么样的结果,一般分为三种(最大值,最小值,个数),这个是目标,同样很重要。
第二个就是状态计算,即如何划分你的集合,简单来说就是归纳总结(数学),你当前的状态是什么,前一个状态是什么,根据属性完成状态推导。
本题来说,
- 当前状态即 f [ i ] [ j ] f[i][j] f[i][j],
- 前一个状态即 f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j] 和 f [ i + 1 ] [ j + 1 ] f[i+1][j+1] f[i+1][j+1](这些不是凭空想象出来的,而是根据给出的三角形进行合理推导的),
- 属性即最大值,
故可以得出结论,并完成代码的书写。
写法2 O ( n 2 ) O(n^2) O(n2)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
int n;
int f[N][N];
int main()
cin >> n;
for (int i = 1; i <= n; ++ i )
for (int j = 1; j <= i; ++ j )
cin >> f[i][j];
for (int i = n - 1; i >= 0; -- i )
for (int j = 1; j <= i; ++ j )
f[i][j] += max(f[i+1][j], f[i+1][j+1]);
cout << f[1][1] << endl;
return 0;
最后提交,AC😁
推导证明
解法2与解法1的本质是一样的,只是为了简化代码,与算法思想无关。
- 首先可以合并数组,即去掉w数组;
- 然后是去除w数组所在位置;
- 最后合并f数组。
举一反三
可以进行多种dp题目的训练,后续可能会不定时更新。
总结
继续努力,坚持更新,2nd打卡。
以上是关于寒假每日一题数字三角形(个人练习)详细题解+推导证明(第二天)的主要内容,如果未能解决你的问题,请参考以下文章
寒假每日一题找硬币(个人练习)详细题解+推导证明(第十二天)
寒假每日一题回文平方(个人练习)详细题解+推导证明(第五天)