问题描述
小U决定在一个 的地图上行走。地图中的每个位置都有一个高度,表示地形的高低。小U只能在满足以下条件的情况下移动:
- 只能上坡或者下坡,不能走到高度相同的点。
- 移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡。
- 每个位置只能经过一次,不能重复行走。
任务是帮助小U找到他在地图上可以移动的最大次数,即在符合所有条件的前提下,小U能走过的最大连续位置数量。
问题分析
读题时
问题的核心是路径搜索,显然可以用 DFS。每次从一个点出发,尝试四个方向移动,对于每个移动,需要判断高度变化是否满足上/下坡交替的条件,同时确保每个点只能访问一次。每次递归返回前需要回溯,将当前点重新标记为未访问状态。为了找到全地图的最长路径,需要从每个点分别尝试作为起点进行搜索,并记录最长路径。每个点最多被递归访问一次,地图大小为 ,加上每个点的四个方向的尝试,总体复杂度约为 ,可以接受。
解题时
方向
我采用了 directions 数组存放四对 Pair ,每个 Pair 包含两个整数,分别表示当前点在行和列方向上的位移量。
这样设计的好处是,四个方向的位移量可以统一存储在一个数组中,无需为每个方向单独编写逻辑。而且如果未来需要增加其他方向(例如对角线移动),只需要在 directions 中添加新的位移即可,无需修改核心逻辑。
这种方向数组是路径搜索问题中非常经典的技巧,适用于各种棋盘类或网格类问题,能让代码干净很多。实际使用时,只需要遍历该方向数组,在循环内实现dfs即可,很方便。
上下坡检查
因为要求上下坡交替,所以我为 dfs 函数加了一个up参数,每次递归调用时传入反值,这样就较为方便地解决了,避免了上下坡情况分开时的重复逻辑。同时,判断部分的代码逻辑也就很清晰了,上/坡时判断下一步高度是否高/低于当前位置即可。
代码分析及注释
#include <bits/stdc++.h>
using namespace std;
int solution(int m, int n, vector<vector<int>> &a) {
vector<vector<bool>> visited(m, vector<bool>(n, false));
function<int(int, int, bool)> dfs = [&](int x, int y, bool up) -> int {
int max_path = 0;
// 方向数组
vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 标记当前位置已访问
visited[x][y] = true;
// 尝试向四个方向移动
for (auto& dir : directions) {
int next_x = x + dir.first;
int next_y = y + dir.second;
// 检查是否到边界,是否已访问
if (next_x >= 0 && next_x < m && next_y >= 0 && next_y < n &&
!visited[next_x][next_y]) {
// 检查是否满足上/下坡条件
if ((up && a[next_x][next_y] < a[x][y]) ||
(!up && a[next_x][next_y] > a[x][y])) {
max_path = max(max_path, dfs(next_x, next_y, !up));
}
}
}
// 回溯,清除标记
visited[x][y] = false;
return max_path + 1;
};
int max_step = 0;
// DFS
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
max_step = max(max_step, dfs(i, j, true));
max_step = max(max_step, dfs(i, j, false));
}
}
return max_step - 1;
}
int main() {
// 测试用例,此处省略
return 0;
}
总结
该题是一道比较常规的搜索问题。如何把上下坡交替结合进递归操作,这个思维点虽然不难,但也算该题的重点之一,如果对递归不够了解的话可能不容易马上想到。