数字三角形

题目理解

这道题目描述了一个数字金字塔结构,要求从顶部到底部找到一条路径,使得路径上的数字之和最大。每一步可以选择移动到左下方或右下方的数字。

输入要求:

  • 第一行一个整数r,表示金字塔的行数

  • 接下来r行,每行包含对应行数的整数,构成数字金字塔

输出要求:

  • 一个整数,表示可能得到的最大路径和

解题思路

1. 问题分析

这是一个典型的动态规划问题,具有以下特点:

  • 最优子结构:整体最优解包含子问题的最优解

  • 重叠子问题:计算过程中会重复访问相同的子问题

  • 无后效性:当前状态只与之前状态有关,与之后状态无关

2. 递推关系

我们可以从下往上思考,也可以从上往下思考。这里采用从上往下的动态规划方法

  1. 状态定义a[i][j]表示到达第i行第j列位置时的最大路径和

  2. 初始状态a[1][1]就是金字塔顶端的数字

  3. 状态转移方程

    • 对于第i行第j列的位置,它可以从第i-1行的第j-1列或第j列移动而来

    • 因此,a[i][j] = max(a[i-1][j-1], a[i-1][j]) + 当前数字

  4. 最终结果:遍历最后一行的所有a[n][j],取最大值

3. 算法优化

实际上,我们可以在填充a[i][j]的同时记录当前最大值,这样就不需要最后再遍历一次最后一行。这就是代码中ans = max(ans, a[i][j])的作用。

参考代码

#include<bits/stdc++.h>
using namespace std;

int a[1005][1005], n, ans; // a数组存储金字塔及动态规划结果,n是行数,ans记录最大值

int main() {
    // 读取输入
    cin >> n; // 读取金字塔的行数
    
    // 动态规划处理
    for (int i = 1; i <= n; i++) { // 遍历每一行
        for (int j = 1; j <= i; j++) { // 遍历当前行的每个数字
            cin >> a[i][j]; // 读取当前数字
            
            // 状态转移方程:
            // 当前数字加上上方两个数字中较大的一个
            a[i][j] = max(a[i - 1][j], a[i - 1][j - 1]) + a[i][j];
            
            // 更新全局最大值
            ans = max(ans, a[i][j]);
        }
    }
    
    // 输出结果
    cout << ans;
    return 0;
}

复杂度分析

  • 时间复杂度:O(n²),其中n是金字塔的行数。因为我们需要处理n行,第i行有i个元素。

  • 空间复杂度:O(n²),用于存储金字塔和动态规划结果。

示例推演

以题目中的样例为例,我们来看动态规划的过程:

初始金字塔:

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

动态规划过程:

  1. 第一行:a[1][1] = 7

  2. 第二行:

    • a[2][1] = max(0, 7) + 3 = 10 (左边没有元素视为0)

    • a[2][2] = max(7, 0) + 8 = 15

  3. 第三行:

    • a[3][1] = max(0, 10) + 8 = 18

    • a[3][2] = max(10, 15) + 1 = 16

    • a[3][3] = max(15, 0) + 0 = 15

  4. 以此类推...

最终得到的最大路径和为30,对应路径7→3→8→7→5

总结

这道题目展示了动态规划在解决最优化问题中的强大能力。通过定义合适的状态和状态转移方程,我们可以高效地解决看似复杂的问题。这种自顶向下的动态规划方法直观易懂,是解决金字塔/三角形类问题的经典方法。

posted @ 2025-05-07 15:42  行胜于言Ibl  阅读(30)  评论(0)    收藏  举报