C语言练习题目(六)----数字三角形

一、题目要求如下

二、解题思路

题目要求:

        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;
}

-----------------------------------------------------------------------------------

码题不易,跪求点赞!

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值