记忆化搜索

给定一个 RR 行 CC 列的矩阵,表示一个矩形网格滑雪场。

矩阵中第 ii 行第 jj 列的点表示滑雪场的第 ii 行第 jj 列区域的高度。

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

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

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

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−124−17−2−1。

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

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

输入格式

第一行包含两个整数 RR 和 CC。

接下来 RR 行,每行包含 CC 个整数,表示完整的二维矩阵。

思路:

算法分析 本来是一个dfs的过程,遍历所有的位置,找到从当前位置往下走的最大路径,再取最大值,可是这样做会有很多重复的位置被重新计算过,因此可以利用空间换时间的思想,把遍历过的位置往下走的路径的最大值进行记录,这就是记忆化搜索

注意:f二维数组初始化的时候最好统一赋值为-1,如果不进行初始化直接用0判断,此题可以,可是如果遇到一些记忆化搜索的问题要求方案数的时候,初始化是0可能会导致个别情况计算出来的恰好结果是0时,却被认为未遍历过,因此统一赋值为-1就没错了

时间复杂度 O(n2)

算法2 使用记忆化数组 记录每个点的最大滑动长度 遍历每个点作为起点 然后检测该点四周的点 如果可以滑动到其他的点 那么该点的最大滑动长度 就是其他可以滑到的点的滑动长度+1 dpi = max(dpi, dpi,dpi-1,dpi+1)

由于滑雪是必须滑到比当前低的点 所以不会存在一个点多次进入的问题 如果该点的dp 不为初始化值 那么就说明计算过 不必再次计算。

按照上述公式 代码如下

#include <iostream>
#include <algorithm>
using namespace std;
​
const int N = 310;
int R, C;
​
int arr[N][N];
int dp[N][N];
​
int maxlen = -1;
​
int addx[] = { 1,-1,0,0 };
int addy[] = { 0,0,1,-1 };
​
int  Dfs(int x, int y)
{
    if (dp[x][y] != 0) return dp[x][y];
​
​
    for (int i = 0; i < 4; i++) {
        int newx = x + addx[i];
        int newy = y + addy[i];
    
        if (newx >= 0 && newx < R && newy >= 0 && newy < C &&
            arr[newx][newy] < arr[x][y])
        {
            dp[x][y] = max(dp[x][y], Dfs(newx, newy) + 1);
        }
    }
    return dp[x][y];
​
}
​
int main()
{
    ios::sync_with_stdio(false);
​
    cin >> R >> C;
    
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
            cin >> arr[i][j];
        }
    }
    
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
            int len = Dfs(i, j);
            maxlen = max(maxlen, len);
        }
    }
    
    cout << maxlen + 1 << endl;
    
    return 0;
​
}
​
​
​作者:itdef 链接:AcWing 901. 滑雪 - AcWing 来源:AcWing 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

y总代码

#include<cstring>
#include<iostream>
#include<algorithm>
​
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];//&是引用符号,意味所有写v的地方等价为写f[x][y]
    if(v!=-1) return v;//如果v不为-1表示v已经被算过了
    
    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&&v<=m&&h[a][b]<h[x][y])//当满足ab这个点存在且高度小于xy这个点时才可以到达
            v=max(v,dp(a,b)+1);
    }
    return v;
}
​
int main(){
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",h[i][j]);
        }
    }
    memset(f,-1,sizeof f);//初始化所有路径长度为f
    
    int res=0;
    for(int i=1;i<=n;i++){
        for(Int j=1;j<=m;j++){
            res=max(res,dp(i,j));//返回是ij的状态返回
        }
    }
    
    printf("%d\n",res);
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值