一、题目要求如下
二、解题思路
题目要求:
1、求一条路线总和最大的值
2、只能走左下或右下最近的结点
3、向左走和向右走的总次数不能超过1(左和右的移动次数相差为0或者1)
解题的思路可以使用:动态规划
我们使用一个二维数组num[100][100]来存放数字信息和位置信息;
数组num的下标含义与值的含义:num【第几行】【第几个】=这个点的值
例如num[0][0]=7;num[2][0]=8;
再创建一个二维数组sum[100][100]来存放到该点的最大路径总和;
数组sum的下标含义与值的含义:sum【第几行】【第几个】=到这个节点最大路径总和的值
例如sum[0][1]=11(7+3); sum[2][1]=16(7+8+1);
求状态
分析得出一共会有三种状态。
第一种状态:
如图显示,若当前状态处于8,则只能由3-8。而3也只能从7到3。说明若结点处于【?】【0】时,必然路线只有一条,就是一路向左下走得到。
则8点的sum就应该是sum[2][0]=sum[1][0]+num[2][0] (当前结点总和=上一个结点总和+当前结点值)
推广得到状态方程:(j=0时) sum[i][j]=sum[i-1][j]+num[i][j];
第二种状态:
如图所示,和第一种类似,若结点是最后一位,只能是从左上的结点8来的。即一路都是向右下走。
但值得注意的是,数组上限容易没有边界,【?】【边界】这个边界应该为当前的行数i,即当结点处于【?】【i】时,最大路径总和唯一,且一定是往右下一直走得到。
则0点的sum值应该为sum[2][2]=sum[1][1]+num[2][2];
推广得到状态方程应为(j=i时)sum[i][j]=sum[i-1][j-1]+num[i][j];
第三种状态:
如图所示,当状态处于中间时,即非以上两种状态时,他可能是从左上来的,也可能是从右上来的。这时候需要比较大小,也就是比较左上的总值大还是右上的总值大。所以在完成这一步总值计算之前先声明一个函数max,用来比较值的大小。
当状态处于1时,我们需要从3和8中选择值比较大的那个进行运算
sum[2][1]=max(sum[1][0],sum[1][1])+num[2][1];
推广得到状态方程:(当j!=0 && j!=i时)sum[i][j]=max(sum[i-1][j-1],sum[i-1][j])+num[i][j];
当有了状态方程后,我们能通过从1~n开始赋值i和从0~i来赋值j实现状态规划。能求出每个结点的最大路径。
i为什么从1开始,因为i等于0是指第一行,第一行默认就是一个数字,不需要做判断
j为什么小于等于i,因为j=i时恰好就是最后一个结点。
两层for循环,每次执行内容是一个if条件判断,分别是三种状态的情况,当j==0、当j==i、当j!=i &&j!=0
以上就可以通过状态规划算出每个结点最大的路径值
有了每个结点的最大路径,我们还要求最后一行结点中最大的 最大路径。即真正意义上的整个图的最大路径。
创建一个sum_max的整型变量,for循环0~n(最后一行下标从0到n)若sum中的值比sum_max要大,则刷新sum_max。
这样就能实现找到最后一行最大的数字了。
但是还有一个题意没有满足,即左走和右走的次数差不能超过1。什么意思呢?我们观察图形。若我需要知道5的路径左走了多少、右走了多少。我可以画出全部的路径来观察规律
不难看出,三条黄色路径都能从7到5,并且仔细观察发现,无论哪一条路径,左走的次数和右走的次数是相同的!!
即每个结点左右移动的次数是固定的。
再观察容易发现,5结点所处的位置是最后一行第二个,左边有一个结点,右边有三个结点。这刚好对应着左走三步,右走一步就能到达。
辅助记忆就是,在最后一行上,从左到右需要走一步到该结点,即该结点总共有右移次数1。从该行最右走到该结点需要走三步,即该结点共左移3。
而题目要求左右移动次数不超过1,观察发现,偶数行中,只有中间两个结点满足要求。奇数行中,只有中间那一个结点满足要求。可以通过一个if判断来先判断是奇数行还是偶数行,再进行左右移动是否满足的判断
最终得到的sum_max即为我们需要的最大路径
三、代码实现
#include <iostream>
using namespace std;
int max(int a,int b){
if(a>b) return a;
return b;
}
int main()
{
int sum[100][100],num[100][100],i,j,n,sum_max=0;
scanf("%d",&n);//获取行数n
//获取数组
for(i=0;i<n;i++){
for(j=0;j<=i;j++){
scanf("%d",&num[i][j]);
}
}
//动态规划
sum[0][0]=num[0][0];
for(i=1;i<n;i++){
for(j=0;j<=i;j++){
if(j==0) sum[i][j]=sum[i-1][j]+num[i][j];
else if(j!=0 && j!=i) sum[i][j]=max(sum[i-1][j-1],sum[i-1][j])+num[i][j];
else sum[i][j]=sum[i-1][j-1]+num[i][j];
}
}
//判断最后一行的路线总和大小,并挑选出符合题目要求的路线
for(i=0;i<n;i++){
if(sum_max<sum[n-1][i]) sum_max=sum[n-1][i];
if(n%2==1 && i==n/2) sum_max=sum[n-1][i];
else if(n%2==0 && (i==n/2||i==n/2-1)) sum_max=sum[n-1][i];
}
}
printf("%d\n",sum_max);
return 0;
}
-----------------------------------------------------------------------------------
码题不易,跪求点赞!