P1162 填涂颜色

记录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)与逆向思维结合的问题。

  1. 问题转化(逆向思维)
    题目要求找出闭合圈内部的 0 并涂成 2。直接去搜索内部区域是非常困难的,因为闭合圈形状任意,很难确定一个绝对在内部的起点。
    因此,我们采用逆向思维:既然内部很难找,那我们就去标记外部!只要是从矩阵边界出发,能够走到的所有 0,一定都在闭合圈的外部。剩下的、没有被标记过的 0,就必然是闭合圈内部的 0,将它们涂成 2 即可。

  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 << " "在输出阶段直接完成涂色无需修改原数组,节省了空间,逻辑更加清晰
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值