题目:
901. 滑雪 - AcWing题库https://www.acwing.com/problem/content/903/
题目:
给定一个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 <= R, C <= 300.
0 <= 矩阵中整数 <= 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
思路分析:
思路分析来自acwing~ 整理成文字:
状态表示: f[i,j]
-
集合: 所有从(i,j)开始滑的路径
-
属性: max 所有路径的最大长度
状态计算:
-
分情况讨论的过程, 把f[i,j]表示的所有路径以向哪个方向滑分成四类, ↑→↓←
代码:
代码来自 acwing y总, 根据讲解添加了注释方便复习:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 310;
int n, m; //矩阵的行和列
int h[N][N];//h表示的是矩阵上每个点的高度
int f[N][N]; //表示动态规划的dp的所有状态 从 h[i][j] 位置开始滑的路径的最大值
// 方向向量: 左上右下
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; // != -1代表 该f[x][y]已经计算过了 就直接返回答案
//没计算过的话就算一下v
v = 1;//初始化一下v 最少能走当前这个格子
//枚举上下左右四个方向
for (int i = 0; i < 4; i++)
{
// 下一个位置
int a = x + dx[i], b = y + dy[i];
// 必须在边界范围内 1<=a<=n, 1<=b<=m, 新的位置低于上一个位置h[ab] < h[xy]
if (a >= 1 && a <= n && b >= 1 && b <= m && h[a][b] < h[x][y])//在这个范围内 才能走到新的点[a,b]
v = max(v, dp(a, b) + 1); // 取最大值(原f[x,y], 新的dp(a,b)+最后一个点), 是从(x,y)不断走到底的过程
}
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]);//读取矩阵每个点的高度
//把每个状态初始化成-1, -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));
printf("%d\n", res);//返回答案 不能直接调用f[i,j] 因为f[i,j]没有求出来
return 0;
}