AcWing 898. 数字三角形

题目传送门

0、听课笔记

\(IOI\)原题,世界高中生信息学竞赛

需要进省队,进国家队,每年有4人代表中国参加\(IOI\)竞赛。

这道题如此简单,是因为是上世纪\(IOI\)的原题。

1、dfs

#include <bits/stdc++.h>

using namespace std;

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

//定义:计算以x,y为根的子树,返回子树的最长路径
//分析:任何一个x,y,要想获得答案,都需要了解它的左右儿子的情况,判断左右儿子哪个大,然后再把大的+自己就是答案
int dfs(int x, int y) {
    //左儿子,x+1,y
    //右儿子,x+1,y+1
    if (x == n + 1) return 0;//当走到n+1层,下面就不再有左右儿子了,所以是递归的出口,可以返回0
    //左子树最长路径计算:dfs(x+1,y)
    //右子树最长路径计算:dfs(x+1,y+1)
    return max(dfs(x + 1, y), dfs(x + 1, y + 1)) + a[x][y];
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            scanf("%d", &a[i][j]);
    //计算以1,1为根的子树最长路径
    int res = dfs(1, 1);
    //输出
    printf("%d", res);
    return 0;
}

结果:简单样例通过

本题数据范围
\(1≤n≤500, −10000≤\)三角形中的整数\(≤10000\)

因为爆搜的时间复杂度是 \(2^{n-1}\),所以\(500\)的话就是\(2^{499}\),肯定是\(TLE\)了!需要想办法优化。

2、记忆化搜索

#include <bits/stdc++.h>

using namespace std;
const int N = 510;

int a[N][N];
int f[N][N];
int n;

int dfs(int x, int y) {
    //如果算过,就直接返回
    if (~f[x][y]) return f[x][y];
    //左下方,x+1,y
    //右下方,x+1,y+1
    if (x == n + 1) return 0;
    //算过的都记录下来,别丢掉
    f[x + 1][y] = dfs(x + 1, y);
    f[x + 1][y + 1] = dfs(x + 1, y + 1);
    return max(f[x + 1][y], f[x + 1][y + 1]) + a[x][y];
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            scanf("%d", &a[i][j]);

    //因为数字三角形中的数字都是大于0,所以初始化为-1,用来记录哪些位置搜索过,哪些还没有
    memset(f, -1, sizeof f);

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

3、DP 动态规划

动态规划
(1)状态表示
\(f[i][j]\)

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

(2)状态计算
\(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 = 1010;
int f[N][N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= i; j++)
            scanf("%d", &f[i][j]);
    //自下向上,上面的准备依赖下面的,那么提前准备好下面的数据信息
    //如果求的是第n-1行的某个数字为根构成的数字三角形,那么问题该怎么处理呢?
    /*1、怎么初始化?
     其实base case是我们需要注意的一个问题:f[n][1]~f[n][n]根据实际要求,
     应该就是现在的本身,因为没有下一层。所以,本来可能需要进行一下base
     case初始化的,这里也不需要了。
     2、为什么是倒着循环的?
     因为我们发现f[i]其实是依赖于f[i+1]的,是先有f[i+1],再有f[i],这还不倒序还能怎么办?
     正序倒序主要是看题意的实际情况。
    */
    for (int i = n - 1; i >= 1; i--)
        for (int j = 1; j <= i; j++)
            //此时枚举,遍历到的数据都是真实的数据,可以直接使用递推出上层的数字
            f[i][j] += max(f[i + 1][j], f[i + 1][j + 1]);

    //输出结果
    printf("%d", f[1][1]);
    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15425343.html