Project_Euler-18 题解

Project_Euler-18 题解

标题

题目

1
2
3
4

题目中数据:

75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

思路

题目要求是让我们找到和最大的路径,对于每一个元素,假设他们下面两个方向路径上的值已经确定(递归思想),那么我们只需要选择两条路径中较大的一条路径再加上这个元素就可以了。

接下来再看一下数据在数组中的表示形式,这和二叉树类似,但是不是二叉树。
表示形式

用表格会更直观一些:

012345
(0,0)
(1,0)(1, 1)
(2,0)(2, 1)(2, 2)

代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define ll long long
#define MAX_N 15

int a[MAX_N + 5][MAX_N + 5];

// i代表层,j代表列,n是最大层数
int dfs(int i, int j, int n) {
	// 如果下一层超出了最大层(数据从下标0开始),就之间返回
    if (i + 1 == n) return a[i][j];
    // 获取该元素下两条最大路径
    int val1 = dfs(i + 1, j, n);
    int val2 = dfs(i + 1, j + 1, n);
    // 取得两条路径的较大者,然后加上自己,返回
    return (val1 > val2 ? val1 : val2) + a[i][j];
}

int main() {
    int ans;
    // 数据需要使用另一个文件重定向输入
    for (int i = 0; i < MAX_N; i++) {
        for (int j = 0; j <= i; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    ans = dfs(0, 0, MAX_N);
    printf("ans = %d", ans);
    return 0;
}

不使用递归的话也可以,从数据尾部之间向上搜索:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define MAX_N 15
#define max(a, b) ({\
    __typeof(a) _a = a;\
    __typeof(b) _b = b;\
    (_a) > (_b) ? (_a) : (_b);\
})


int a[MAX_N + 5][MAX_N + 5];


int main() {
    for (int i = 0; i < MAX_N; i++) {
        for (int j = 0; j <= i; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    
    for (int i = MAX_N - 2; i >= 0; i--) {
        for (int j = 0; j <= i; j++) {
            a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
        }
    }
    printf("%d\n",a[0][0]);
    return 0;
}

优化思路

我们尝试优化一下递归的代码(因为比较方便)。

自己观察数据发现会有重复查找的迹象:

red
自己观察,发现所有红色区域的元素都背搜寻了两遍,但是我们只是想要寻找该元素下方两个方向上的最大路径,因此完全没必要再向下查询,我们可以直接记录下这个元素下最大路径值,等到使用时直接返回答案即可。

优化代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define ll long long
#define MAX_N 15

int a[MAX_N + 5][MAX_N + 5];
int keep[MAX_N][MAX_N]; // 记录一个keep数组,用于记录当前元素下方最大路径值

int dfs(int i, int j, int n) {
    if (i + 1 == n) return a[i][j];
    // 如果没有查找过(为0)则继续查找,否则说明已经搜寻过,直接返回keep数组中的值即可
    if (keep[i][j]) return keep[i][j];
    int val1 = dfs(i + 1, j, n);
    int val2 = dfs(i + 1, j + 1, n);
    // 说明这个元素的最大值已经找到了,要记录一下
    return keep[i][j] = (val1 > val2 ? val1 : val2) + a[i][j];
}

int main() {
    int ans;
    for (int i = 0; i < MAX_N; i++) {
        for (int j = 0; j <= i; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    ans = dfs(0, 0, MAX_N);
    printf("ans = %d", ans);
    return 0;
}
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若亦_Royi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值