小U最大连续移动次数问题BFS

75 阅读3分钟

引言

今日下午晴,约朋友骑车。绕着梦湖骑了两圈,水波浮动加上一路上下坡,把烦劳都抛掷脑后。回家找青训营刷道算法题,正好找到一道情景相似题

题目:

image.png

算法思路

  1. 首先,题目要每次移动要 连续并且判断是否上下坡变化,然后要找图中能够走过的最大连续数量位置,从某个起点开始的最大连续移动位置想到图里面的DFS ;
  2. 可以把DFS模版进行修改:增加判断上下坡判断;

算法步骤:

先DFS模板给出,再按题意进行修改

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

// 定义方向数组,表示上下左右四个方向
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};

// 网格的大小
int n, m;

// 网格数据
vector<vector<int>> grid;

// 访问标记数组
vector<vector<bool>> visited;

// 当前最大连续移动位置的数量
int maxMoves = 0;

// 当前移动次数
int currentMoves = 0;

// DFS 函数
void dfs(int x, int y) {
    // 更新最大移动次数
    currentMoves++;
    maxMoves = max(maxMoves, currentMoves);

    // 标记当前节点为已访问
    visited[x][y] = true;

    // 遍历四个方向
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];

        // 检查新位置是否在网格范围内且未被访问
        if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) {
            dfs(nx, ny);
        }
    }

    // 回溯,恢复当前节点的访问状态
    visited[x][y] = false;
    currentMoves--;
}

// 主函数
int main() {
    // 读取网格大小
    cin >> n >> m;

    // 初始化网格和访问标记数组
    grid.resize(n, vector<int>(m));
    visited.resize(n, vector<bool>(m, false));

    // 读取网格数据
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }

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

    // 输出最大连续移动位置的数量
    cout << "最大连续移动位置的数量: " << maxMoves << endl;

    return 0;
}

本题完整代码:

#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<int> dx = {-1,1,0,0};
       vector<int> dy = {0,0,-1,1};
       // 遍历四个方向
       for(int i=0;i<4;++i) {
          int nx = x +dx[i];
          int ny = y +dy[i];

          // 检查边界
          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
                  maxPath = max(maxPath,dfs(nx,ny,!isUp));
              }
          }
       }
       // 回溯:标记位置重设为false
       visited[x][y] = false;
       // 返回路径长度(即当前位置到初始位置)
       return maxPath+1;
   };
    int maxSteps = 0;//步数初始0
    // 找初始位置,再使用dfs
    for(int i=0;i<n;++i) {
        for(int j=0;j<m;++j ){
            // isUp 上坡,初始位置上下坡都要选一下试试
            // 移动到低位置
            maxSteps = max(maxSteps,dfs(i,j,true)) ;
            maxSteps = max(maxSteps,dfs(i,j,false));
        }
    }
    // 题目要移动次数,路径减去1
    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;
}

优化

// dx,dy可以换成pair<int,int>
 // 尝试向四个方向移动
        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));
                }
            }
        }

多刷题吧,学了数据结构还是要把理论用到实践的,NOI开打