[题解]洛谷P1434滑雪

洛谷 P1434 滑雪


#0.0 方法

我觉得这道题主要方法应该有两种:

  • 动态规划

  • 搜索

下面会分别对这两种方法进行简述


#1.0 动态规划

#1.1 思路

首先的想法是用 L(i,j)表示从点 (i,j) 出发能到达的最长距离

  • 因为从 (i,j) 出发最少能滑行自己 1 格,所以每个点 L 值都先初始化为 1

  • 我们可以从 (i,j) 出发,向四周寻找,如果四周没有比他低的点,那么 L(i,j) 即为 1,否则 L(i,j) 就为从 (i,j) 出发四周高度比 L 低且 L 值最大的那个点 PL 值加 1

递推时的顺序为点的高度由低到高,那么在递推过程中,计算 L(i,j)时,他四周比他低的点P的L值一定已经被计算出来了

接下来需要解决的问题便是如何按点的高度由小到大递推

关于这个问题,我们难道要每次递推时用两遍循环找出当前未递推的最低点?

显然不可取,时间复杂度会爆炸的。那么就考虑能否一个sort()解决问题懒得自己写排序)。sort()能给一个二维数组排序?不知大佬们怎么想,反正本蒟蒻不会。。。那为了sort()更香可以方便的使用,我用一个一维数组进行存储每个点的数据。

那么,**结构体 **成为我们的第一选择

struct Point{
    int r; //行号
    int c; //列号
    int h; //高度
    bool operator < (const Point & p) const {
        return h < p.h;
    }//构造函数,不懂的同学可以写一个cmp代替,下面代码会有特别说明
};
Point point[10101];

为了方便储存以及不造成浪费个人习惯)这里 ij 都从 0 开始取,那么点 (i,j)point数组存的下标为 c×i+j 由于 i,j0 开始存,c×i+j 这个式子表示的是第 i+1 行第 j+1 个数,那么,i 最大为 R1j 最大为 C1,整个数组最大下标为C×(R1)+C1,即 C×R1,若 i,j1开始取,为保证运算方便及善待空间,下标应为 C×(i1)+j1

该算法的分析已给出,大家可以结合代码自行理解

#1.2 代码

#include <iostream>//头文件,不多解释
#include <cstdio>
#include <algorithm>
using namespace std;
struct Point{
    int r;//行号
    int c;//列号
    int h;//高度
    bool operator < (const Point & p) const {
        return h < p.h;//构造函数,不会的可删掉看下面cmp
    }
};Point point[10101];
int cmp(Point a,Point b){
    if (a.h < b.h)   //如果a的高度小于b的高度
      return 1;//返回真
    else
      return 0;//否则返回假
}
int R,C,ans = -0x3ffff;//需初始化ans为较小值(害怕玄学出错)
int a[101][101],d[102][102];//a为每点的高度,d为该点可滑行的最大值
int main(){
    cin >> R >> C;
    for (int i = 0;i < R;i ++)
      for (int j = 0;j < C;j ++){
        cin >> a[i][j];//输入点的高度
        point[i * C + j].h = a[i][j];//根据上面所说的下标,
        point[i * C + j].r = i;      //对点的数据进行存储
        point[i * C + j].c = j;
        d[i][j] = 1;            //初始可滑行长度为1
      }
    sort(point,point + R * C);//如果用了cmp可换为sort(point,point + R * C,cmp);
    for (int i = 0;i < R * C;i ++){
        int r = point[i].r;//找一个替身,怕玄学或运算时出错
        int c = point[i].c;//同理
        if (r > 0 && a[r - 1][c] < a[r][c])//找上面的
          d[r][c] = max(d[r][c],d[r - 1][c] + 1);//进行更新
        if (c > 0 && a[r][c - 1] < a[r][c])//找左面的
          d[r][c] = max(d[r][c],d[r][c - 1] + 1);//进行更新
        if (r < R - 1 && a[r + 1][c] < a[r][c])//找下面的
          d[r][c] = max(d[r][c],d[r + 1][c] + 1);//进行更新
        if (c < C - 1 && a[r][c + 1] < a[r][c])//找右面的
          d[r][c] = max(d[r][c],d[r][c + 1] + 1); //进行更新
    }
    for (int i = 0;i < R;i ++)
      for (int j = 0;j < C;j ++)
        ans = max(ans,d[i][j]); //找出最大值
    cout << ans;
    return 0;//完美撒花
}

#1.3 时间复杂度

由于点 (i,j)L 值的计算为极小的常数值,所以这个算法的复杂度为 O(R×C)

#2.0 搜索

相信大家都会想到DFS,但若是单纯的DFS,会TLE一个点,所以,我们就要考虑到记忆化搜索。那么也很好实现,具体请见下方代码:

#include <iostream>
#include <cstdio>
using namespace std;
int R,C;
int g1[4] = {0,1,0,-1};//定义寻找的4个方向
int g2[4] = {-1,0,1,0};
int m[101][101];//储存每个点的高度
int lj[101][101];//储存每个点可滑行的最大长度
int maxn,lr;
int dfs(int i,int j){
    if (lj[i][j] - 1)//判断是否搜过,因为下面将每一个点的最初滑行长度为1
                 //如果该点最长长度-1为0,说明该点未被搜过
      return lj[i][j];
    for (int o = 0;o < 4;o ++)//向4个方向搜
      if (i + g1[o] >= 0 && i + g1[o] < R && j + g2[o] >= 0 && j + g2[o] < C && m[i + g1[o]][j + g2[o]] < m[i][j]){//保证不越界
        lj[i][j] = max(lj[i][j],dfs(i + g1[o],j + g2[o]) + 1);//进行搜索及更新
      }
    return lj[i][j];
}
int main(){
    cin >> R >> C;
    for (int i = 0;i < R;i ++)//初始化
      for (int j = 0;j < C;j ++)
        lj[i][j] = 1;
    for (int i = 0;i < R;i ++)
      for (int j = 0;j < C;j ++)
        cin >> m[i][j];
    for (int i = 0;i < R;i ++)
      for (int j = 0;j < C;j ++){
        if (lj[i][j] - 1 != 0){//如果该点被搜过
            maxn = max(maxn,lj[i][j]);//直接更新
            continue;
          }
        lj[i][j] = dfs(i,j);
        maxn = max(maxn,lj[i][j]);
      }
    cout << maxn;
    return 0;//撒花
}

这就是该题本人的思路,希望能给你带来帮助

更新&说明

  • 初次发布 - 2019.11.25

  • 更换 markdown 编辑器 - 2021.2.19

    持续修改完善

posted @   Dfkuaid  阅读(305)  评论(4编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
点击右上角即可分享
微信分享提示