1.写在前面
🎈 恭喜你发现了一片新的天地,来到阿亮的世界欸,欢迎欢迎 🎉 🎉 🎉
🍬 亮亮我欸,是一名在学C++、JAVA、HTML的大一菜菜捏,新的一年,会不定期更新Java或C++、stl及其算法之间的文章 ✍ ✍ ✍
❓ 由于阿亮刚写博客不久,本文肯定有遗漏疏忽的地方,欢迎大家批评指正!
⭐ 这是亮全新开辟的一个章节,会不定期更新C++新颖的知识点和一些算法题。目前阶段所做的题还很简单,不会很难,但会坚持下去每天一点点学习新的东西,相信总有一天自然水到渠成!
2.题干信息
这里我就直接放图里的题目了诶,就不码字了,这样节省一下时间哈哈哈哈哈哈哈(绝对不是因为我懒)
3.分析解题思路
- 题目的大概意思是说给一个数字排成的三角形,由第一行的一个数向左下或者右下走到到第二行的一个数,再从第二行的这个数再向左下或者右下走到第三行的一个数,再从第三行的这个数……最后从倒数第二行的一个数向左下或者右下走到最后一行的一个数。题目最后要求的就是,把走过的数加在一起最大的值,而且有个限制条件就是 “向左下走的次数与向右下走的次数相差不能超过1”(即向左或向右走的次数要么相等,要么左=右+1,要么右=左+1)
- 但由于计算机读入的空格不算数字,所以最后输入进去的三角形其实是n行n个值的一个直角三角形,所以用代码体现左下时相当于直接向下(即行数+1,列数不变),右下时(行数+1,列数+1),后面就不再说左下了,直接说向下,要不然怕大家搞混。
- 怎么来说明左右次数差不超过1呢?
①我们可以对三角形由第一行的那个值走到下面每个值时向下和右下的次数来找关系(按样例举个例子来看):
②注:设次数坐标(x,y)为(向下次数,右下次数)
n=1 (0,0)
n=2 (1,0) (0,1) // (向下走1,右下走0) (向下走0,右下走1)
n=3 (2,0) (1,1) (0,2) //(向下走2,右下走0) (向下走1,右下走1) (向下走0,右下走2)
n=4 (3,0) (2,1) (1,2) (0,3)
n=5 (4,0) (3,1) (2,2) (1,3) (0,4)
n=6 (5,0) (4,1) (3,2) (2,3) (1,4) (0,5)
③继续进行深入分析,不难看出:
当n为奇数,次数坐标x与y不超过1时,最后一行只能走到中间的值
(n=5时,最后只能走第3个次数坐标为(2,2)的值)
当n为偶数,次数坐标x与y不超过1时,最后一行只能走中间的两个值(n=6时,最后要么走第3个次数坐标为(3,2)的值,要么走第4个次数坐标为(2,3)的值)
④总结:从左上到右下依次计算最大值并保存(或者从右下到左上保存最大值也行,这里我已经把从右下到左上的写完通过了,代码放在最后代号为DM2,想从左上到右下和大家再一起写一遍),从左上往右下走到最后一行时,对最后一行进行判断,若为奇数行,输出再加上最后一行的中间数;若为偶数行,输出再加上最后一行中间两个数较大的数。
- 分析问题找最优解,找出最优解的性质,并进行递归
①最后要找的是走到第n行的最优解,就需要找到走到第n-1行的最优解,再加上第n行的最优值
②要找到第n-1行的最优解,需要找到第n-2行最优解,再加上第n-1行的最优值
③以此类推,最后遍历到第一行的最优解即为第一行的那一个值
④代码展示最优解就是开一个d[i][j] (代表第i行第j列得到的最优值) 即为d[i][j]=max(d[i-1][j], d[i-1][j-1])+ai][j] (这里的a[i][j]表示的是原三角形i行j列的值,所以④整体表现的意思就是:走到i行j列的最优值=i-1行两种走法最大的一个加上走到的这个值)
4.代码展示
4.1 DM1(从上往下)
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int a[101][101],dp[201][201]; //原三角形放在a[n][n]里,最优解放在dp[n][n]里
int max(int a,int b){
return a>b?a:b;
}
int main()
{
int n;
scanf("%d",&n);
memset(a,-127,sizeof(a)); //将两个数组初始化全为-127
memset(dp,-127,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=0;i<=n;i++){ //第一行
dp[1][i]=a[1][i];
}
for(int i=2;i<=n;i++){ //从第二行开始遍历到最后一行
for(int j=1;j<=i;j++){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j];
}
}
if(n%2==0) //如果n为偶,输出中间两个里最大的那个
printf("%d\n",max(dp[n][n/2],dp[n][n/2+1]));
else //如果n为奇,输出最中间的一个
printf("%d\n",dp[n][(n+1)/2]);
return 0;
}
4.2 DM2(从下往上)
这个是我第一遍写的,做了一上午才做来QAQ
#include<bits/stdc++.h>
int max(int x,int y){
return x>y?x:y;
}
int main()
{
int n;
scanf("%d",&n);
int a[n][n],dp[n][n];
memset(a,-2,sizeof(a));
memset(dp,-2,sizeof(dp));
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
if(n%2==0){
dp[n-1][n/2]=a[n-1][n/2];
dp[n-1][(n-1)/2]=a[n-1][(n-1)/2];
}
else
dp[n-1][n/2]=a[n-1][n/2];
for(int i=n-2;i>=0;i--){
for(int j=0;j<=i;j++){
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
}
}
printf("%d\n",dp[0][0]);
return 0;
}
这个是我们班大佬写的,更优化了一些,挺好的,给大家也分享一下叭~
#include<bits/stdc++.h>
using namespace std;
int a[101][101],f[200][200];
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
scanf("%d",&a[i][j]);
memset(f,-127,sizeof(f));
for (int i=1;i<=n;i++)
f[n][i]=-2147483647;
if (n%2==0)
{
f[n][n/2]=a[n][n/2];
f[n][n/2+1]=a[n][n/2+1];
}
else
f[n][n/2+1]=a[n][n/2+1];
for (int i=1;i<=n;i++)
{
f[i][0]=-2147483647;
f[i][i+1]=-2147483647;
}
for (int i=n-1;i>=1;i--)
for (int j=1;j<=i;j++)
f[i][j]=max(f[i][j],(max(f[i+1][j],f[i+1][j+1])+a[i][j]));
printf("%d",f[1][1]);
}
5.祝福
🧨 最后,新的一年,提前预祝大家兔年大吉!愿所念之人,平安喜樂,所想之事,顺心如意!愿新的一年,你我都能远离疫病,敢于直面攀登过程中的流言蜚语,亦不怕掉落低谷时的迷茫彷徨。让我们一起加油,共同进步叭!
平安顺遂,萬事如意,
祝你,祝我,祝我们~