青训营X豆包MarsCode 技术训练营小U最大连续移动次数 | 青训营笔记创作活动

133 阅读3分钟

问题描述: image.png

解题思路: 问题理解
我们需要在一个 m x n 的地图上找到一条路径,满足以下条件:

只能上坡或者下坡,不能走到高度相同的点。
移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡。
每个位置只能经过一次,不能重复行走。
目标是找到在符合所有条件的前提下,小U能走过的最大连续位置数量。

数据结构选择

二维数组 a:表示地图,存储每个位置的高度。

二维布尔数组 visited:记录每个位置是否已经被访问过。

算法步骤
DFS遍历:

从地图的每个位置开始,尝试进行深度优先搜索(DFS)。
在DFS过程中,根据当前位置的高度和上一次移动的方向(上坡或下坡),决定下一步的移动方向。
每次移动后,更新当前路径的长度,并记录最大路径长度。
回溯:

在DFS过程中,如果某个位置的所有可能移动方向都已经被探索过,则回溯到上一个位置,继续探索其他方向。 回溯时需要将当前位置标记为未访问,以便从其他路径再次访问该位置。
边界条件:

在DFS过程中,需要检查是否越界,以及是否已经访问过该位置。
代码如下所示:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
 
int solution(int m, int n, vector<vector<int>>& a) {
    // 初始化visited数组
    vector<vector<bool>> visited(m, vector<bool>(n, false));
    
    // 定义DFS函数
    function<int(int, int, bool)> dfs = [&](int x, int y, bool isUp) -> int {
        // 标记当前位置为已访问
        visited[x][y] = true;
        
        int maxPath = 0;
        
        // 尝试向四个方向移动
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        for (auto& dir : directions) {
            int nx = x + dir.first;
            int ny = y + dir.second;
            
            // 检查边界和是否已访问
            if (nx >= 0 && nx < m && ny >= 0 && ny < n && !visited[nx][ny]) {
                // 检查是否满足上坡或下坡条件
                if ((isUp && a[nx][ny] < a[x][y]) || (!isUp && a[nx][ny] > a[x][y])) {
                    // 递归调用DFS
                    maxPath = max(maxPath, dfs(nx, ny, !isUp));
                }
            }
        }
        
        // 回溯:标记当前位置为未访问
        visited[x][y] = false;
        
        // 返回当前路径长度
        return maxPath + 1;
    };
    
    int maxSteps = 0;
    
    // 遍历地图中的每个位置,尝试从该位置开始DFS
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            maxSteps = max(maxSteps, dfs(i, j, true));
            maxSteps = max(maxSteps, dfs(i, j, false));
        }
    }
    
    return maxSteps - 1;
}
 
int main() {
    vector<vector<int>> a1 = {{1, 2}, {4, 3}};
    cout << (solution(2, 2, a1) == 3) << endl;
 
    vector<vector<int>> a2 = {{10, 1, 6}, {5, 9, 3}, {7, 2, 4}};
    cout << (solution(3, 3, a2) == 8) << endl;
 
    vector<vector<int>> a3 = {{8, 3, 2, 1}, {4, 7, 6, 5}, {12, 11, 10, 9}, {16, 15, 14, 13}};
    cout << (solution(4, 4, a3) == 11) << endl;
}

本文中在采用深度优先搜索时,同时当前位置四个方向进行深度遍历,同时通过附加以恶个布尔型变量到深度优先遍历函数里面,来保证函数能够进行递增和递减交替进行,即上山和下山交替进行。

学习感悟:
递归思想:DFS通常使用递归实现,理解递归函数的调用过程对于理解DFS至关重要。
状态标记:在DFS过程中,需要标记已访问过的位置,防止重复访问,通常使用标记来实现上山和下山交替的目的 回溯:在搜索过程中,需要回溯到上一步,撤销当前的选择,继续探索其他可能性,直到找到解或者遍历完所有可能性。
问题抽象:将实际问题抽象成图的形式,然后应用DFS算法进行解决,培养了问题建模和算法应用的能力。
通过学习DFS算法,可以更好地理解递归思想、回溯算法以及图遍历的原理,对于解决与路径、组合优化相关的问题有很大帮助。不断练习DFS算法,并结合其他算法思想,可以提升编程能力和解决实际问题的能力。