记录158
#include<bits/stdc++.h>
using namespace std;
int mp[35][35];
bool vis[35][35];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int n;
void dfs(int x,int y){ //从地图不涉及的外围来进行渗透
vis[x][y]=1;
for(int i=0;i<4;i++){
int nx=x+dx[i];
int ny=y+dy[i];
if(nx>=0&&nx<=n+1&&ny>=0&&ny<=n+1&&vis[nx][ny]==0&&mp[nx][ny]==0){
vis[nx][ny]=1;
dfs(nx,ny);
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>mp[i][j];
}
}
dfs(0,0); //提前染色外部区域
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(vis[i][j]==0&&mp[i][j]==0) cout<<2<<" ";
else cout<<mp[i][j]<<" ";
}
cout<<"\n";
}
return 0;
}
//从扩大地图从最外层开始的原因,特殊情况
//1 0 1
//0 1 0
//1 0 1
//预防这种情况
题目传送门
https://www.luogu.com.cn/problem/P1162
前言
我是一名专注信奥赛(CSP-J/S、NOIP)的教练。
- 如果你觉得这篇题解对你有帮助,欢迎点击关注我的CSDN账号,我会持续更新高质量算法解析。
- 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
- 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。
核心解题思路
这道题是一道非常经典的网格图搜索(DFS/BFS)与逆向思维结合的问题。
-
问题转化(逆向思维):
题目要求找出闭合圈内部的 0 并涂成 2。直接去搜索内部区域是非常困难的,因为闭合圈形状任意,很难确定一个绝对在内部的起点。
因此,我们采用逆向思维:既然内部很难找,那我们就去标记外部!只要是从矩阵边界出发,能够走到的所有 0,一定都在闭合圈的外部。剩下的、没有被标记过的 0,就必然是闭合圈内部的 0,将它们涂成 2 即可。 -
算法设计(虚拟边界与 DFS 渗透):
为了处理边界上的 0,我们在原矩阵的外围人为地“扩大”一圈,将搜索的起点设置在(0,0)这个虚拟的外围点。从(0,0)出发进行深度优先搜索(DFS),只要遇到 0 且没有越界,就继续向上下左右四个方向渗透,并用vis数组将这些外部 0 标记为已访问。搜索结束后,遍历原矩阵,凡是mp[i][j]==0且vis[i][j]==0的点,就是被圈住的内部 0。
代码分块详细解释
1. 全局变量与方向数组定义
#include<bits/stdc++.h>
using namespace std;
int mp[35][35];
bool vis[35][35];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int n;
- 详细分析:
mp数组用于存储输入的 0/1 矩阵,vis数组用于记录哪些格子已经被搜索过(即属于外部区域)。dx和dy是经典的四方向偏移数组,分别代表上、右、下、左四个方向的坐标变化量。
2. 核心逻辑:DFS 外部渗透
void dfs(int x, int y){ // 从地图不涉及的外围来进行渗透
vis[x][y] = 1;
for(int i = 0; i < 4; i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx >= 0 && nx <= n+1 && ny >= 0 && ny <= n+1 && vis[nx][ny] == 0 && mp[nx][ny] == 0){
vis[nx][ny] = 1;
dfs(nx, ny);
}
}
}
- 详细分析:这是本题的精髓。
- 越界与边界处理:注意
if条件中的边界判断是nx >= 0 && nx <= n+1。因为原矩阵是1到n,所以0和n+1是人为扩大的虚拟边界。这保证了搜索可以在外围畅通无阻。 - 渗透条件:只有当目标点在合法范围内、未被访问过(
vis[nx][ny]==0),且是 0(mp[nx][ny]==0)时,才能继续渗透。遇到 1(闭合圈的墙壁)则自动被阻挡。
- 越界与边界处理:注意
3. 主函数:数据读入与启动搜索
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> mp[i][j];
}
}
dfs(0, 0); // 提前染色外部区域
- 详细分析:读入 n×n的矩阵。随后直接调用
dfs(0, 0),从虚拟的左上角外围点开始,将所有与外界连通的 0 全部标记为外部区域。
4. 结果输出与内部填充
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(vis[i][j] == 0 && mp[i][j] == 0) cout << 2 << " ";
else cout << mp[i][j] << " ";
}
cout << "\n";
}
return 0;
}
- 详细分析:遍历原矩阵的 1 到 n 范围。如果当前点是 0 且没有被
vis标记过(说明它既不是外部的 0,也不是 1 墙壁),那么它一定是闭合圈内部的 0,直接输出 2;否则,原样输出该点的值。
核心逻辑总结表
| 代码模块 | 核心变量/操作 | 精炼作用 | 解决的痛点 |
|---|---|---|---|
| 逆向思维 | 标记外部而非内部 | 将“寻找内部连通块”转化为“寻找外部连通块” | 避免了寻找内部起点困难的问题,外部起点 (0,0) 是绝对安全的 |
| 虚拟边界 | nx >= 0 && nx <= n+1 | 将搜索范围扩大到原矩阵外围的一圈 | 完美处理了原矩阵边界上就是 0 的特殊情况,防止漏判 |
| DFS渗透 | dfs(nx, ny) | 沿着 0 的路径向四周蔓延 | 自动绕开 1(墙壁),精准标记出所有与外界连通的 0 |
| 内部判定 | vis[i][j]==0 && mp[i][j]==0 | 寻找未被标记的 0 | 利用排除法,剩下的 0 必然是被 1 完全包围的内部空间 |
| 原地输出 | cout << 2 << " " | 在输出阶段直接完成涂色 | 无需修改原数组,节省了空间,逻辑更加清晰 |


被折叠的 条评论
为什么被折叠?



