P1162 填涂颜色

1|0P1162 填涂颜色

大概思路,暴搜所有0点,搜到就填色,然后搜色块的时候一旦碰边就打标记,回溯之后看标记决定标0还是标2

思路是理论可行,但是代码容易出问题。

一开始48pts的代码

#include<iostream> #include<algorithm> #include<cstdio> const int N = 31; int n; int map[N][N], vis[N][N]; bool is_circle; int walk[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; using namespace std; void dfs(int x, int y) { if (x == 1 || y == 1 || x == n || y == n) is_circle = false; for (int i = 0; i <= 3; i++) { int dx = x + walk[i][0], dy = y + walk[i][1]; if (dx < 1 || dx > n || dy < 1 || dy > n || map[dx][dy] == 1) continue; if (is_circle && vis[dx][dy] > 1) continue; if (!is_circle && vis[dx][dy]) continue; vis[dx][dy]++; dfs(dx, dy); if (is_circle) vis[dx][dy]++; } } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { scanf("%d", &map[i][j]); } } is_circle = true; dfs(3, 4); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (!map[i][j] && !vis[i][j]) { is_circle = true; vis[i][j]++; dfs(i, j); if (is_circle) vis[i][j]++; } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if(map[i][j] == 1) { printf("1 "); } else if(vis[i][j] > 1){ printf("%d ", vis[i][j]); } else { printf("0 "); } } printf("\n"); } return 0; }

对照数据发现有一些点vis[i][j]>2了,才意识到if (is_circle) vis[dx][dy]++;这是不严谨的,具体原因还没法描述出来,但是修改思路还是很显然,我改成了if (is_circle) vis[dx][dy] = 2;,再次提交,还是有一些点没过。

再对照一些数据点,发现一些本不可以被包围的圈内出现了零散的2,意识到这个算法存在不严谨:

假设一次搜索进入的色块是没有被围住的

那么第一次进入搜索直到第一次触边前,is_circle == true

如此一来,直到第一次触边前所有的vis[x][y]都被理所当然的赋为了2.

知道这个问题之后,要解决就简单了,递归返回到本次函数后,再专门对is_circle进行判断,如果变成了false, 就把所有标记为2vis[x][y]还原为1

AC code

#include<iostream> #include<algorithm> #include<cstdio> const int N = 31; int n; int map[N][N], vis[N][N]; bool is_circle; int walk[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; using namespace std; void dfs(int x, int y) { if (x == 1 || y == 1 || x == n || y == n) is_circle = false; for (int i = 0; i <= 3; i++) { int dx = x + walk[i][0], dy = y + walk[i][1]; if (dx < 1 || dx > n || dy < 1 || dy > n || map[dx][dy] == 1) continue; if (is_circle && vis[dx][dy] > 1) continue; if (!is_circle && vis[dx][dy]) continue; vis[dx][dy]++; dfs(dx, dy); if (is_circle) vis[dx][dy] = 2; } if (!is_circle) { for (int i = 0; i <= 3; i++) { int dx = x + walk[i][0], dy = y + walk[i][1]; if (dx < 1 || dx > n || dy < 1 || dy > n || map[dx][dy] == 1) continue; vis[dx][dy] = 1; } } } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { scanf("%d", &map[i][j]); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (!map[i][j] && !vis[i][j]) { is_circle = true; vis[i][j]++; dfs(i, j); if (is_circle) vis[i][j] = 2; } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if(map[i][j] == 1) { printf("1 "); } else if(vis[i][j] > 1){ printf("%d ", vis[i][j]); } else { printf("0 "); } } printf("\n"); } return 0; }

虽然如此,但显然这个做法是不优秀的,发现题解中有一个很妙的方法。

先看代码

#include <bits/stdc++.h> using namespace std; int a[32][32],b[32][32]; int dx[5]={0,-1,1,0,0}; int dy[5]={0,0,0,-1,1};//第一个表示不动,是充数的,后面的四个分别是上下左右四个方向 int n,i,j; void dfs(int p,int q){ int i; if (p<0||p>n+1||q<0||q>n+1||a[p][q]!=0) return;//如果搜过头或者已经被搜过了或者本来就是墙的就往回 a[p][q]=1;//染色 for (i=1;i<=4;i++) dfs(p+dx[i],q+dy[i]);//向四个方向搜索 } int main(){ cin>>n; for (i=1;i<=n;i++) for (j=1;j<=n;j++){ cin>>b[i][j];//其实不拿两个数组也可以,不过我喜欢啦 if (b[i][j]==0) a[i][j]=0; else a[i][j]=2; } dfs(0,0);//搜索 从0,0开始搜 for (i=1;i<=n;i++){ for (j=1;j<=n;j++) if (a[i][j]==0) cout<<2<<' ';//如果染过色以后i,j那个地方还是0,说明没有搜到,就是周围有墙,当然就是被围住了,然后输出2 else cout<<b[i][j]<<' ';//因为被染色了,本来没有被围住的水和墙都染成了1,所以就输出b[i][j] cout<<'\n';//换行 } }

这个代码有两个关键思路

  • 逆向思维
    • 只从外层进行搜索染色,那么所有没有被染色到的显然就是被墙围住的。
  • 构造外圈
    • 如果用for循环枚举外层的0进行搜索则不够优雅
    • 直接把存地图时存1N然后在0,n+1构造外层,等于说用一个大边围住了整个地图,而这个边是可通过的,因此只要从(0,0)开始搜就行,不用找其他外层点。

妙哉。


__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17797109.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示