AcWing 898. 数字三角形

AcWing 898. 数字三角形

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

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

输入格式
第一行包含整数 n,表示数字三角形的层数。

接下来 n 行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。

输出格式
输出一个整数,表示最大的路径数字和。

数据范围
1n500,10000三角形中的整数10000

输入样例:

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

输出样例:

30

二、dfs

#include <bits/stdc++.h>

using namespace std;

const int N = 510;
int a[N][N];
int n;

/**
 深度优先搜索关键点:

 1、递归出口
 本题是超出n行,来到了第n+1行,就意味着搜索结束,可以统计答案了

 2、每一个位置,面临两种选择,分别是正下方和右下方,需要计算它们每一条路线的总和,
 sum(left,right,a[x][y])

 3、需要使用int返回,因为从每个坐标出发,最终都需要返回一个sum和,而这个sum对于调用者
 是有用的,不用采用全局变量,只能用返回int形式。

 4、向下传递,用参数;向上传递,用返回值
 */
int dfs(int x, int y) {
    if (x == n + 1) return 0; // 你们不要指望我了,我是0,你们该咋办就咋办吧~
    if (y > x) return 0;
    return max(dfs(x + 1, y), dfs(x + 1, y + 1)) + a[x][y];
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];

    int res = dfs(1, 1);
    printf("%d", res);
    return 0;
}

结果:简单样例通过

本题数据范围
1n500,10000三角形中的整数10000

因为爆搜的时间复杂度是 2n1,所以500的话就是2499,肯定是TLE了!需要想办法优化。

三、记忆化搜索

#include <bits/stdc++.h>

using namespace std;
const int N = 510;
int a[N][N];
int f[N][N]; // 记录以(x,y)坐标位置为根的子树的路径最大和
int n;

int dfs(int x, int y) {
    if (f[x][y]) return f[x][y]; // 如果算过,就直接返回
    if (x == n + 1) return 0;    // 你们不要指望我了,我是0,你们该咋办就咋办吧~
    if (y > x) return 0;
    // 算过的都记录下来,别丢掉,左下方,x+1,y,右下方,x+1,y+1
    f[x + 1][y] = dfs(x + 1, y);
    f[x + 1][y + 1] = dfs(x + 1, y + 1);
    f[x][y] = max(f[x + 1][y], f[x + 1][y + 1]) + a[x][y];
    return f[x][y];
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];
    int res = dfs(1, 1);
    printf("%d", res);
    return 0;
}

四、DP 动态规划 [自底向上+二维数组]

  • 状态表示
    f[i][j]

    集合:从底向上 走到(i,j)所有路线的集合
    属性:max

  • 状态计算
    f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j]

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n, a[N][N];
int f[N][N];
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];

    for (int i = n; i >= 1; i--) // 从下向上去递推
        for (int j = 1; j <= i; j++)
            f[i][j] = max(f[i + 1][j], f[i + 1][j + 1]) + a[i][j];

    printf("%d\n", f[1][1]);
    return 0;
}

五、DP 动态规划 [自底向上+一维数组]

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int f[N];
int a[N][N];
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];

    for (int i = n; i >= 1; i--)
        for (int j = 1; j <= i; j++)
            f[j] = max(f[j + 1], f[j]) + a[i][j];

    printf("%d", f[1]);
    return 0;
}

六、DP 动态规划 [自上向下+二维数组]

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

const int N = 510;
const int INF = 0x3f3f3f3f;
int f[N][N];
int a[N][N];
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];

    memset(f, -0x3f, sizeof f); // 预求最大,先设最小
    // 其实自上而下版本之所以需要初始化-INF,是因为每个枚举到的位置需要从它头顶和它的左上侧取值对比
    // 而它的正上方可能是不存在的,如果不初始化,就是0
    // 本题数据还可能是负数,就会造成它可会从正上方取来一个0做为最大值,造成结果错误
    f[1][1] = a[1][1];
    for (int i = 2; i <= n; i++)
        for (int j = 1; j <= i; j++)
            f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];

    int res = -INF;
    for (int i = 1; i <= n; i++) res = max(res, f[n][i]);
    printf("%d\n", res);
    return 0;
}

七、DP 动态规划 [自上向下+一维数组]

#include <bits/stdc++.h>
using namespace std;
const int N = 510;
const int INF = 0x3f3f3f3f;
int n;
int f[N];
int a[N][N];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            cin >> a[i][j];
    // 预求最大,先设最小
    memset(f, -0x3f, sizeof f);
    f[1] = a[1][1]; // 递推的初始化边界
    for (int i = 2; i <= n; i++)
        for (int j = i; j >= 1; j--)
            f[j] = max(f[j - 1], f[j]) + a[i][j];

    int res = -INF;
    for (int i = 1; i <= n; i++) res = max(res, f[i]);
    printf("%d", res);
    return 0;
}
posted @   糖豆爸爸  阅读(416)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
Live2D
点击右上角即可分享
微信分享提示