刷题实践:C++ 用DFS和回溯法解决《小U的最大连续移动次数问题》| 豆包MarsCode AI刷题

67 阅读5分钟

问题描述

题目描述了一个 m×n 的地图,每个位置有一个高度值。小U需要在这个地图上行走,但有以下限制:

  1. 小U只能上坡或下坡,不能走到高度相同的点。
  2. 移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡。
  3. 每个位置只能经过一次,不能重复行走。

任务是帮助小U找到他在地图上可以移动的最大次数,即在符合所有条件的前提下,小U能走过的最大连续位置数量。 例如在以下2x2的地图中:

1 2
4 3

中庸行者可以选择移动顺序 3 -> 4 -> 1 -> 2,最大移动次数为3。

题目要求

  • 输入:地图的大小 m 和 n,以及地图的高度矩阵 a。
  • 输出:小U可以移动的最大次数。

思路解析

1. 深度优先搜索(DFS)

由于题目要求每个位置只能经过一次,并且移动时必须交替进行,可以使用深度优先搜索(DFS)来解决这个问题。DFS可以有效地遍历所有可能的路径,并记录每条路径的长度。

2. 状态表示

在DFS过程中,需要记录以下几个状态:

  • 当前位置 (x,y)
  • 是否处于上坡状态(is_up
  • 已访问的位置(visited
3. 递归终止条件
  • 如果当前位置超出地图范围,递归终止。
  • 如果当前位置已经访问过,递归终止。
4. 递归过程
  • 对于每个位置,尝试向四个方向(上、下、左、右)移动。
  • 检查新位置是否在地图范围内且未被访问过。
  • 检查新位置的高度是否满足上坡或下坡的要求。
  • 如果满足条件,继续递归,并更新最大路径长度。
  • 递归结束后,回溯:标记当前位置为未访问。
int dfs(int m, int n, vector<vector<int>> &a, vector<vector<bool>> &visited, int x, int y, bool is_up)
{
    // 标记当前位置为已访问
    visited[x][y] = true;
    int max_length = 0;

    // 定义四个方向的移动
    vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    for (auto &dir : directions)
    {
        int new_x = x + dir.first;
        int new_y = y + dir.second;

        // 检查新位置是否在地图范围内
        if (new_x >= 0 && new_x < m && new_y >= 0 && new_y < n)
        {
            // 检查新位置是否未被访问过
            if (!visited[new_x][new_y])
            {
                // 检查是否满足高度差条件和交替移动条件
                if ((is_up && a[new_x][new_y] < a[x][y]) || (!is_up && a[new_x][new_y] > a[x][y]))
                {
                    // 继续DFS
                    max_length = max(max_length, 1 + dfs(m, n, a, visited, new_x, new_y, !is_up));
                }
            }
        }
    }

    // 回溯:标记当前位置为未访问
    visited[x][y] = false;
    return max_length;
}
5. 全局最优解
  • 从每个位置开始进行DFS,尝试所有可能的起始点。
  • 记录所有路径的最大长度,最终返回全局最大值。
int solution(int m, int n, vector<vector<int>> &a)
{
    int max_length = 0;
    vector<vector<bool>> visited(m, vector<bool>(n, false));

    // 从每个位置开始进行DFS
    for (int i = 0; i < m; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            max_length = max(max_length, dfs(m, n, a, visited, i, j, true));
            max_length = max(max_length, dfs(m, n, a, visited, i, j, false));
        }
    }

    return max_length;
}

核心知识

1. 深度优先搜索(DFS)

DFS是一种用于遍历或搜索树或图的算法。它从根节点开始,沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。

909637838d486250694877600564a5e.jpg

2. 回溯法

回溯法是一种通过试错来寻找问题解决方案的方法。在解决问题的过程中,如果发现当前选择无法达到目标,则撤销当前选择,回溯到上一步,尝试其他选择。这种方法适用于需要探索所有可能解的问题。

3. 二维数组的遍历

在处理二维数组时,通常需要遍历每个元素。可以通过嵌套循环来实现这一点。对于每个元素,可以尝试向四个方向(上、下、左、右)移动,确保新位置在数组范围内且未被访问过。

4. 交替条件的处理

在DFS过程中,需要维护一个布尔变量 is_up 来记录当前是否处于上坡状态。每次移动时,根据 is_up 的值判断新位置的高度是否满足条件,并在递归调用时切换 is_up 的值。

代码实现

以下是完整的代码实现,包括DFS和主函数:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int dfs(int m, int n, vector<vector<int>>& a, vector<vector<bool>>& visited, int x, int y, bool is_up) {
    // 标记当前位置为已访问
    visited[x][y] = true;
    int max_length = 0;

    // 定义四个方向的移动
    vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    for (auto& dir : directions) {
        int new_x = x + dir.first;
        int new_y = y + dir.second;

        // 检查新位置是否在地图范围内
        if (new_x >= 0 && new_x < m && new_y >= 0 && new_y < n) {
            // 检查新位置是否未被访问过
            if (!visited[new_x][new_y]) {
                // 检查是否满足高度差条件和交替移动条件
                if ((is_up && a[new_x][new_y] < a[x][y]) || (!is_up && a[new_x][new_y] > a[x][y])) {
                    // 继续DFS
                    max_length = max(max_length, 1 + dfs(m, n, a, visited, new_x, new_y, !is_up));
                }
            }
        }
    }

    // 回溯:标记当前位置为未访问
    visited[x][y] = false;
    return max_length;
}

int solution(int m, int n, vector<vector<int>>& a) {
    int max_length = 0;
    vector<vector<bool>> visited(m, vector<bool>(n, false));

    // 从每个位置开始进行DFS
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            max_length = max(max_length, dfs(m, n, a, visited, i, j, true));
            max_length = max(max_length, dfs(m, n, a, visited, i, j, false));
        }
    }

    return max_length;
}

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允许我们遍历所有可能的路径,并记录每条路径的长度。回溯法确保我们在探索完一条路径后,可以回到上一步,尝试其他路径。通过这种方式,我们可以找到小U在地图上可以移动的最大次数。