[刷题之旅no11]P1162 填涂颜色

这篇博客探讨了一个有趣的现象,即对于人眼来说容易识别的迷宫解决方案,计算机实现起来却颇具挑战。作者通过迭代和深度优先搜索(DFS)策略,解决寻找连通块的问题。首先,从迷宫外部添加一圈0,然后从0,0位置开始,遇到墙或已搜索过的点则忽略,否则标记该点并继续搜索其相邻区域。最终未被标记的点即为墙内点,输出即可得到解决方案。博客中还详细介绍了代码实现过程及遇到的问题和改进措施。

越容易用肉眼看出答案的题,用计算机越难实现。。。
人眼是面观察,而计算机是点观察。
一开始的思路就是:
一但遇到了一个内部点,也就是从这个点开始,上下左右遍历,如果四个方向都可以遇到1,那么一定就是内部点。
如果能遇到这样的点,那么直接进行递归涂色即可,非常简单。
所以难点就在于:如何高效地找出一个符合条件的点呢?
所以现在的思想:
从上到下,从左到右遍历迷宫
那么一定先遇到迷宫的左墙。
若第一次遇到1,那么直接跳到下一行,
当下一行再次遇到1的时候,开始对1后面的0进行判断,如果判断成功,那么直接停止遍历就行了,把这个找到的点,直接带入递归式,最后打印迷宫即可。
问题记录:
1.if和else if 只能进入一个,所以如果我想向四周遍历,不能是分支,只要满足条件应该都可以进入才对。
2.每次更新flag=0的值放错了位置
3.双重循环判断条件,把两个字母写重复了。
ok,现在完全没有问题,代码速度也很快!
不过代码还是很长,需要简化,学习一下大佬的思路:
利用dfs,只需要把联通块找出来就好了,让不在墙内的联通再一起就行。
只需要在迷宫外层码一圈0,然后从0,0开始深搜,遇到墙或者遇到边界或者这个点搜过了,那么就不管他了。否则,另找一个数组,把这个对应的点标为1.然后四周深搜就可以了。
这样就可以把所右的墙外点都标记上
最后没被标记的就是墙内的点,对应输出就可以了。
最牛逼的思路就在于在墙外多加上了一圈,保证连通

我的代码:

#include<stdio.h>
int maze[32][32]={0},flag=0,n=0,cnt=0,over=0;//迷宫从1到n为记录
int J_in(int x,int y);
void print(void);
void paint(int x,int y);
int main()
{
	scanf("%d",&n);//读取迷宫大小 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&maze[i][j]);
		}
	}
	//读取信息完毕
	//唯一任务,找到我想要的点。
	for(int i=1;i<=n;i++)//从头开始记录第一个点。 
	{
		for(int j=1;j<=n;j++)
		{
			if(maze[i][j]==1&&cnt==0)//第一次遇到墙
			{
				cnt++;
				break;//这层循环都不进行了。
//				而且cnt将会永远等于1。 
			}
//			现在,只要能在这里遇到1,说明是第二层了,
//			继续向后遍历,然后寻找我们需要的0 
			if(maze[i][j]==1)
			{
				flag=1;
				continue; 
			}
			else if(maze[i][j]==0&&flag==1)
			{
				//这一层根本没进来。。 
//				printf("(%d,%d),%d\n",i,j,J_in(i,j));
				if(J_in(i,j))
				{
//					进入这个判断说明直接结束游戏了
					paint(i,j);
					over=1;
					break;
				}
			}
//			每一层循环结束之后记得
		}
		flag=0;
		if(over)
		{
			break;
		}
	}
	//测试: 
	print();
	return 0;
}

void print(void)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			printf("%d ",maze[i][j]);
		}
		printf("\n");
	}
}

int J_in(int x,int y)
{
	//记住不可以直接遍历x所在的一行,需要分别遍历x的左右
	for(int i=y-1;i>0;i--)//x的左边
	{
		if(maze[x][i]==1)//如果遇到墙直接break; 
		{
			break;
		}
		else if(i==1&&maze[x][i]==0)//到达边缘还不是墙 
		{
			return 0;
		}
	}
	//其他异曲同工
	for(int i=y+1;i<=n;i++)//x的右边 
	{
		if(maze[x][i]==1)//如果遇到墙直接break; 
		{
			break;
		}
		else if(i==n&&maze[x][i]==0)//到达边缘还不是墙 
		{
			return 0;
		}
	}
	for(int i=x-1;i>0;i--)//点的上边 
	{
		if(maze[i][y]==1)//如果遇到墙直接break; 
		{
			break;
		}
		else if(i==1&&maze[i][y]==0)//到达边缘还不是墙 
		{
			return 0;
		}
	}
	for(int i=x+1;i<=n;i++)//x的右边 
	{
		if(maze[i][y]==1)//如果遇到墙直接break; 
		{
			break;
		}
		else if(i==n&&maze[i][y]==0)//到达边缘还不是墙 
		{
			return 0;
		}
	}
	//如果上述都符合,那么直接return 1就好了。 
	return 1;
}

void paint(int x,int y)
{
	//上下左右遍历涂色
	maze[x][y]=2;
	if(maze[x-1][y]==0)
	{
		paint(x-1,y);
	}
	if(maze[x+1][y]==0)
	{
		paint(x+1,y);
	}
	if(maze[x][y-1]==0)
	{
		paint(x,y-1);
	}
	if(maze[x][y+1]==0)
	{
		paint(x,y+1);
	}
	return;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值