二维DP—记忆化搜索—洛—滑雪

P1434 [SHOI2002] 滑雪

题目描述

Michael 喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael 想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:

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-16-1\)(从 \(24\) 开始,在 \(1\) 结束)。当然 \(25-24-23-\ldots-3-2-1\) 更长。事实上,这是最长的一条。

输入格式

输入的第一行为表示区域的二维数组的行数 \(R\) 和列数 \(C\)。下面是 \(R\) 行,每行有 \(C\) 个数,代表高度(两个数字之间用 \(1\) 个空格间隔)。

输出格式

输出区域中最长滑坡的长度。

输入输出样例 #1

输入 #1

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

输出 #1

25

说明/提示

对于 \(100\%\) 的数据,\(1\leq R,C\leq 100\)。

对于任意输入的高度 \(h\),满足 \(0 \le h \le 10000\),且 \(h\) 是整数。

做法一:BFS + 动态规划

想法:从每个点出发,看看能不能跟新周围的点,能跟新的话入队。自上向下。
注意:初始时要把每个点都入队,不能只考虑刚开始只从最高点开始跟新,因为最长路径不一定包括最高点。且每个点的dp初始值都为1。
代码:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int r,c,ans;
int m[105][105],dp[105][105];
bool vis[105][105];    //防止重复入队
struct ty
{
    int x,y;
};
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
queue <ty> q;
int main()
{
    cin>>r>>c;
    int temp = 0;
    for(int i = 1;i <= r;i++)
    {
        for(int j = 1;j <= c;j++)
        {
            cin>>m[i][j];
            dp[i][j] = 1;
            q.push({j,i});
            vis[i][j] = true;
        }
    }
    while(!q.empty())
    {
        ty temp = q.front();
        q.pop();
        int x1 = temp.x, y1 = temp.y;
        vis[y1][x1] = false;
        for(int i = 0;i < 4;i++)
        {
            int dx = x1 + dir[i][0];
            int dy = y1 + dir[i][1];
            if(dx < 1 || dx > c || dy < 1 || dy > r || m[dy][dx] >= m[y1][x1])    continue;
            if(dp[y1][x1] + 1 > dp[dy][dx])
            {
                dp[dy][dx] = dp[y1][x1] + 1;
                if(!vis[dy][dx])
                {
                    q.push({dx,dy});
                    vis[dy][dx] = true;
                }
            }
        }
    }
    for(int i = 1;i <= r;i++)
    {
        for(int j = 1;j <= c;j++)    ans = max(ans,dp[i][j]);
    }
    cout<<ans;
    return 0;
}

做法二:DSF + 动态规划 + 记忆化搜索

想法:常规的二维动态规划都是有一定的顺序,像每行每列的遍历,这里想要这样就必须得dfs递推到最终状态。自下向上。代码更简单,但要注意栈溢出,赋初值,终止搜索,记忆化实现等问题。
代码:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int r,c,ans;
int m[105][105],dp[105][105];
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int dfs(int x,int y)
{
    if(dp[y][x])    return dp[y][x];    //核心:体现记忆化搜索,保证每个点只搜一遍
    for(int i = 0;i < 4;i++)
    {
        int dx = x + dir[i][0];
        int dy = y + dir[i][1];
        if(dx < 1 || dx > c || dy < 1 || dy > r || m[dy][dx] <= m[y][x])   continue;
        dp[y][x] = max(dp[y][x], dfs(dx,dy)+1);
    }
    return dp[y][x] = max(dp[y][x] , 1);    //终止记忆化,最高点只有自己一个点,不能再跟新。等号是从右往左赋值
}

int main()
{
    cin>>r>>c;
    int temp = 0;
    for(int i = 1;i <= r;i++)
    {
        for(int j = 1;j <= c;j++)
        {
            cin>>m[i][j];
        }
    }
    for(int i = 1;i <= r;i++)
        for(int j = 1;j <= c;j++)    dfs(j,i);
    for(int i = 1;i <= r;i++)
    {
        for(int j = 1;j <= c;j++)    ans = max(ans,dp[i][j]);
    }
    cout<<ans;
    return 0;
}

posted @ 2026-07-13 16:23  lucky_lh  阅读(7)  评论(0)    收藏  举报