数字三角形

上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。

路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右 边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。

输入描述

输入的第一行包含一个整数 N\ (1 \leq N \leq 100)N (1≤N≤100),表示三角形的行数。

下面的 NN 行给出数字三角形。数字三角形上的数都是 0 至 100 之间的整数。

输出描述

输出一个整数,表示答案。

输入输出样例

示例
输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出
27

解题思路:

容易弄混的三种思想:

  1. 分治法:将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解决这些问题,然后将各个子问题的解合并得到原问题的解。

  1. 贪心法:当前的选择要依赖于已经做出的选择,但不依赖于有待做出的选择和子问题。因此贪心法是自顶向下,一步步地做出贪心的选择。

  1. 动态规划:将问题实例分析为更小的、相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略。(所针对问题有一个显著的特征,即它所对应的子问题树中的子问题呈现大量的重复)(问题树可以像杨辉三角那样)

这道题为什么用动态规划呢?

首先分解成子问题后,可以用递归,第i层的值等于左边返回值与右边返回值的较大者,加上本身的值(左子树值或右子树值+结点值),但是并不相互独立,因为有向左向右走的限制,如果加上步数,若左子树更大,left+1;若右子树更大,right+1。如果达到顶层后,步数相差大于1,那么就等于白加。如果想在某一步改变走的方向,那么会破坏递归规则的一致性,即不是左右子树最大值加上结点值。而且不确定从哪一步改变方向。所以不适合用递归。

贪心法:每一步都选择了左右边更大的一个,但是若左边的第一个数较小,但是这条路径上的总数更大,就不能实现目标。

动态规划:思想是自底向上,即第i层的结果等于第i-1层的两个来路的值较大者,加上该层本身的值,同时用一个备忘录记录行走过程。计算的时候其实是自顶向下的。即得先知道,上一层的值,才能求出下一层。

需要建立最大值的二维表sum,第i层第j列表示走到当前位置路径上的最大值。

记录路径的二维表roat(叫path好像更好一点),第i层第j列表示,从上一层往该位置走是选择走左边(值为1),还是走右边(值为0).

对 最大值的二维表进行遍历,用二次for循环从左到右进行遍历:有两个特殊位置,若该位置是在边缘(j=0或者j=i),从上一层走到该位置只能走左边或者只能是右边,即最大值只能是正上方或者左上方位置的值+该位置的值。如果在中间,则要对两个来的方向的位置的值进行判断,选最大的来路,并且记录来路的方向。

二维表都建立完成后,在主函数中对最后一层的最大值进行比较,选择左右方向走的次数差值<=1,并且值最大的那个输出。

注意几个易错点:

  1. 上一层对应的位置所在列要注意是j或j+1,还是j或j-1

  1. 保存路径的二维表不需要用结构体(包含left和right值),每个left和right保存只是从上一层到该位置的走法,并没有保存到最终的left和right总值。(这是因为我每次都只是让left或者right++,而不是将之前的值累加,可以变成roat[i][j].left=roat[i][j].left++;z这样就可以避免再重复计算一次总步数了。)

  1. 判断语句里的判断条件别太多,容易错。

#include <iostream>
using namespace std;

int Search(int Arry[][100], int roat[][100], int i, int j, int sum[][100],int N)
{
    for (int i = 1;i <N;i++)
    {
        for (int j = 0;j <= i;j++)
        {
            if (j == 0)
            {
                sum[i][j] = sum[i - 1][j] + Arry[i][j];
                roat[i][j]=1;
            }
            if (j == i)
            {
                sum[i][j] = sum[i - 1][j - 1] + Arry[i][j];
                roat[i][j]=0;
            }
            else
            {
                if (sum[i - 1][j] >= sum[i - 1][j - 1])
                {
                    sum[i][j] = sum[i - 1][j] + Arry[i][j];
                    roat[i][j]=1;
                }
                else
                {
                    sum[i][j] = sum[i - 1][j - 1] + Arry[i][j];
                    roat[i][j]=0;
                }
            }
            
        }

    }
    return 0;
}

int main()
{
    // 请在此输入您的代码
    int N;
    cin >> N;
    int Arry[100][100];
    for (int i = 0;i < N;i++)
    {
        for (int j = 0;j <= i;j++)
        {
            cin >> Arry[i][j];
        }
    }
    int roat[100][100];

    int sum[100][100];
    for (int i = 0;i <100;i++)
    {
        for (int j = 0;j < 100;j++)
        {
            sum[i][j] = 0;
        }
    }
    sum[0][0] = Arry[0][0];
    
    Search(Arry, roat, 0, 0, sum,N);
    
    int max=0;
    
    for (int i = 0;i < N;i++)
    {
        int left=0;
        int right = 0;
        int j =i;
        int pos = N-1;
            while (pos > 0)
            {
                if (roat[pos][j] == 1)
                {
                    left++;
                }
                else
                {
                    right++;
                    j--;
                }
                pos--;
            }
        
        
        if (left - right > 1 || right - left > 1)
            continue;
        else if (max < sum[N-1][i])
            max = sum[N-1][i];
        
    }
    cout << max;
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值