news 2026/5/6 13:34:38

PTA题库‘寻宝图’题解:用DFS搞定连通块与特殊值统计(C++实现)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PTA题库‘寻宝图’题解:用DFS搞定连通块与特殊值统计(C++实现)

PTA题库‘寻宝图’题解:深度优先搜索实战与优化全解析

第一次遇到PTA题库中的"寻宝图"问题时,很多同学会被网格搜索和连通块统计的需求难住。这道题看似简单,却涵盖了DFS算法、边界处理、状态标记等多个核心编程概念。本文将带大家从零开始拆解问题,不仅给出可提交的C++代码,更会深入讲解如何避免常见错误、优化算法效率,以及如何将这类解题思路迁移到其他相似题目中。

1. 问题重述与核心思路

题目给定一个由数字0-9组成的n×m网格,要求统计其中所有非零数字组成的连通区域数量(称为"宝藏区"),并特别统计其中包含数字大于1的连通区域数量。连通的定义是上下左右相邻。

关键解题步骤分解

  1. 网格遍历:逐个检查每个网格点
  2. 连通区域判断:当遇到未被访问的非零点时,启动DFS/BFS
  3. 特殊值检测:在搜索过程中检查是否存在值>1的点
  4. 结果统计:维护两个计数器分别记录总区域数和含特殊值的区域数

注意:题目中的"连通"仅考虑上下左右四个方向,不考虑对角线方向,这点在编写方向数组时需特别注意。

2. DFS实现详解与代码逐行解析

让我们先看完整的C++实现,然后逐步拆解关键部分:

#include <bits/stdc++.h> using namespace std; const int MAXN = 1005; // 根据题目约束调整 vector<vector<int>> grid; vector<vector<bool>> visited; int specialRegions = 0, totalRegions = 0; int n, m; // 方向数组:上、右、下、左 const int dx[] = {-1, 0, 1, 0}; const int dy[] = {0, 1, 0, -1}; void dfs(int x, int y, bool& hasSpecial) { visited[x][y] = true; if (grid[x][y] > 1) hasSpecial = true; for (int i = 0; i < 4; ++i) { int nx = x + dx[i], ny = y + dy[i]; if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && grid[nx][ny] != 0) { if (grid[nx][ny] > 1) hasSpecial = true; dfs(nx, ny, hasSpecial); } } } int main() { cin >> n >> m; grid.assign(n, vector<int>(m)); visited.assign(n, vector<bool>(m, false)); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { char c; cin >> c; grid[i][j] = c - '0'; } } for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (!visited[i][j] && grid[i][j] != 0) { bool hasSpecial = false; totalRegions++; dfs(i, j, hasSpecial); if (hasSpecial) specialRegions++; } } } cout << totalRegions << " " << specialRegions << endl; return 0; }

关键代码段解析

  1. 方向数组

    const int dx[] = {-1, 0, 1, 0}; // 行变化 const int dy[] = {0, 1, 0, -1}; // 列变化

    这种定义方式比单独写四个if更简洁高效,也便于后续扩展为八方向搜索。

  2. DFS核心逻辑

    void dfs(int x, int y, bool& hasSpecial) { visited[x][y] = true; if (grid[x][y] > 1) hasSpecial = true; for (int i = 0; i < 4; ++i) { int nx = x + dx[i], ny = y + dy[i]; if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && grid[nx][ny] != 0) { dfs(nx, ny, hasSpecial); } } }

    注意边界检查(nx, ny)和访问标记的判断顺序,这是避免数组越界的关键。

  3. 主循环中的统计逻辑

    if (!visited[i][j] && grid[i][j] != 0) { bool hasSpecial = false; totalRegions++; dfs(i, j, hasSpecial); if (hasSpecial) specialRegions++; }

    每次发现新的连通区域时,先增加总区域数,在DFS过程中检测是否有特殊值。

3. 常见错误与调试技巧

在实现DFS算法时,初学者常会遇到以下几类问题:

边界条件处理不当

  • 忘记检查数组越界,导致运行时错误
  • 错误地将边界条件写成nx > 0而不是nx >= 0
  • 行列索引混淆(特别是在行列从0开始还是1开始不统一时)

访问标记问题

  • 忘记标记已访问状态,导致无限递归
  • 错误地在DFS返回前清除访问标记(适用于回溯问题,但不适用于连通区域统计)
  • 使用单独的标记数组而不是修改原数组值(后者可以节省空间)

递归深度问题

  • 当网格很大时(如1000×1000),递归实现可能导致栈溢出
  • 没有及时剪枝,处理包含大量连通点的区域时效率低下

输入处理陷阱

  • 题目输入可能使用字符而非数字直接表示网格值
  • 行尾可能有空白字符需要处理
  • 网格行列顺序与常规数学坐标系不一致

调试建议:对于DFS问题,可以在递归调用前打印当前位置和状态,或者使用条件断点来跟踪特定位置的搜索过程。

4. 算法优化与性能对比

原始DFS实现已经能够解决问题,但我们还可以从多个角度进行优化:

空间优化方案

优化方法实现方式节省空间代码复杂度
原地标记使用grid值0表示已访问省去visited数组需保留原始零值信息
位压缩用bitset代替bool数组减少8倍内存访问稍复杂
分块处理只加载部分网格到内存适合超大网格增加I/O复杂度

时间优化策略

  1. 迭代式DFS

    stack<pair<int, int>> s; s.push({x, y}); visited[x][y] = true; while (!s.empty()) { auto [cx, cy] = s.top(); s.pop(); if (grid[cx][cy] > 1) hasSpecial = true; for (int i = 0; i < 4; ++i) { int nx = cx + dx[i], ny = cy + dy[i]; if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && grid[nx][ny] != 0) { visited[nx][ny] = true; s.push({nx, ny}); } } }

    使用显式栈替代递归,避免栈溢出风险。

  2. 并行化处理: 对于超大网格,可以将网格分区后使用多线程处理不同区域。需要注意:

    • 区域边界处的连通块可能被分割
    • 需要线程安全的计数机制
    • 实际效果取决于硬件核心数
  3. 早期终止: 如果只需要判断是否存在特殊值区域,可以在发现第一个后提前终止搜索。

性能对比测试

在1000×1000网格上的测试结果(单位:毫秒):

方法全零网格随机稀疏网格全连通网格
递归DFS1245栈溢出
迭代DFS1043620
并行BFS(4线程)822180

5. 相似题型与扩展应用

掌握网格DFS后,可以解决许多类似问题:

变种题型

  1. 统计连通区域的最大面积
  2. 查找两个点是否在同一连通区域
  3. 计算需要改变多少个点才能使整个网格连通
  4. 查找特殊形状的连通区域(如L形、T形)

实际应用场景

  • 图像处理中的连通组件分析
  • 游戏中的地图探索系统
  • 社交网络中的群体发现
  • 电路板上的短路检测

推荐练习题

  • PTA: "岛屿数量"、"最大黑区域"
  • LeetCode: 200. Number of Islands, 695. Max Area of Island
  • Codeforces: "Two-dimensional Maze"

在解决这些扩展问题时,DFS的核心思路不变,但需要根据具体需求调整:

  • 维护额外的统计信息(如区域面积)
  • 修改连通条件(如允许对角线连接)
  • 增加记忆化存储避免重复计算
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/6 13:33:54

DS4Windows实用指南:在Windows上专业使用PS4/PS5手柄的3步配置方案

DS4Windows实用指南&#xff1a;在Windows上专业使用PS4/PS5手柄的3步配置方案 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows DS4Windows是一款功能强大的开源手柄兼容工具&#xff0c;专…

作者头像 李华
网站建设 2026/5/6 13:29:40

不加班了!Gemini 3.1 Pro生成PPT汇报大纲实测

概要Gemini 3.1 Pro 是 Google DeepMind 于 2026 年 2 月发布的旗舰大语言模型&#xff0c;采用 MoE 混合专家架构&#xff0c;支持 100 万 token 上下文窗口和原生多模态处理。ARC-AGI-2 得分 77.1%&#xff0c;是上一代的两倍多。本文用一个开发者最头疼的真实场景做实测&…

作者头像 李华
网站建设 2026/5/6 13:26:40

AI原生开发闭环:human_test()实现自动化真人可用性测试与修复

1. 项目概述&#xff1a;当AI开发遇上真人测试 最近在折腾一个挺有意思的项目&#xff0c;叫 human_test() 。这名字听起来像个函数调用&#xff0c;实际上它也确实是一个可以被AI智能体&#xff08;Agent&#xff09;直接调用的“技能”。简单来说&#xff0c;它解决了一个A…

作者头像 李华
网站建设 2026/5/6 13:26:40

3步终极指南:如何永久免费使用Cursor AI编程助手Pro功能

3步终极指南&#xff1a;如何永久免费使用Cursor AI编程助手Pro功能 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your t…

作者头像 李华