算法学习笔记(50)——记忆化搜索

记忆化搜索

题目链接:AcWing 901. 滑雪

题目描述

给定一个 \(R\)\(C\) 列的矩阵,表示一个矩形网格滑雪场。

矩阵中第 \(i\) 行第 \(j\) 列的点表示滑雪场的第 \(i\) 行第 \(j\) 列区域的高度。

一个人从滑雪场中的某个区域内出发,每次可以向上下左右任意一个方向滑动一个单位距离。

当然,一个人能够滑动到某相邻区域的前提是该区域的高度低于自己目前所在区域的高度。

下面给出一个矩阵作为例子:

 1  2  3  4 5

16 17 18 19 6

15 24 25 20 7

14 23 22 21 8

13 12 11 10 9

在给定矩阵中,一条可行的滑行轨迹为 \(24−17−2−1\)

在给定矩阵中,最长的滑行轨迹为 \(25−24−23−…−3−2−1\),沿途共经过 \(25\) 个区域。

现在给定你一个二维矩阵表示滑雪场各区域的高度,请你找出在该滑雪场中能够完成的最长滑雪轨迹,并输出其长度(可经过最大区域数)。

输入格式

第一行包含两个整数 \(R\)\(C\)

接下来 \(R\) 行,每行包含 \(C\) 个整数,表示完整的二维矩阵。

输出格式

输出一个整数,表示可完成的最长滑雪长度。

数据范围

\(1 \le R,C \le 300\),
\(0 \le 矩阵中整数 \le 10000\)

输入样例

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

输出样例

25

状态表示

\(f(i,j)\) 表示所有从\((i,j)\)这个格子开始滑的路径的最大长度。

状态转移

按照第一步是往哪个方向滑的,将所有的路径分为四类(上下左右)

\[f(i, j) = \max[f(i-1,j), f(i+1,j), f(i,j-1),f(i,j+1)] + 1 \]

一般情况下写代码,经常关注的是时间复杂度和空间复杂度,而还需要考虑的一点是代码复杂度。记忆化搜索的代码复杂度很低,采用了递归形式。

#include <iostream>
#include <cstring>

using namespace std;

const int N = 310;

int n, m;
int h[N][N];
int f[N][N];

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int dp(int x, int y)
{
    int &v = f[x][y];
    if (v != -1) return v;
    
    // 最短情况下可以走当前所在的格子,所以初始化为1
    v = 1;
    for (int i = 0; i < 4; i ++ ) {
        int a = x + dx[i], b = y + dy[i];
        if (a >= 1 && a <= n && b >= 1 && b <= m && h[a][b] < h[x][y])
            v = max(v, dp(a, b) + 1);
    }
    
    return v;
}

int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            cin >> h[i][j];
    
    // 初始化为-1表示所有状态都还没有计算过
    memset(f, -1, sizeof f);
    
    int res = 0;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            res = max(res, dp(i, j));
            
    cout << res << endl;
    
    return 0;
}
posted @ 2023-01-07 19:01  S!no  阅读(23)  评论(0编辑  收藏  举报