搜索的实例——水管工游戏

这是一个用深搜解题的例子。

【题目描述】

最近小哈又迷上一个叫水管工的游戏。游戏的大致规则是这样的。一块矩形的土地上被分为 n*m 的单位正方形,现在这块土地上已经埋有一些水管,水管将从坐标(1,1)的矩形土地的左上角左部边缘,延伸到坐标为(n,m)的矩形土地右下角右部边缘。水管只有两种,如下图所示:

 每种管道将占据一个单位正方形土地。你现在可以旋转这些管道,使其构成一个管道系统,即创造一条从(1,1,)到(n,m)的连通管道。标有树木的方格表示这里没有管道。如下图表示一个 5*4 的土地中(2,4)处有一颗树木。

 我们要旋转其中的一些管道,使之构成一个连通的系统,如下图:

【输入】

输入的第一行为两个整数 n 和 m(都不超过 10),接下来的 n 行,每行有 m 个整数,表示地图的每一小格。其中 0 表示树木,1~6 分别表示管道的六种不同的摆放方式,如下:

【输出】

如果可以连通,就输出连通管道铺设的路径,否则输入No

样例输入

5 4
5 3 5 3
1 5 3 0
2 3 5 1
6 1 1 5
1 5 5 4

样例输出

(1,1) (1,2) (2,2) (3,2) (3,3) (3,4) (4,4) (5,4)

 解题思路

首先思考水管怎么连接,然后摆放位置要怎么调整,怎样用搜索才能实现?

水管只有两种形状,水管的摆放有六种,直型水管有两种摆放方法,弯型水管有四种摆放方法。

在搜索时,要考虑每一种摆放类型,有些地方的水管只能用一种类型连接,否则会连接失败,像这样:

 

首先确定得用深搜,广搜难以确定水管进水口的方向和出水口的方向。

那要怎样实现搜索呢?

由于进水口对于搜索也有影响,那就用记号分别代表这几个进水口的方向就好了:

如果进水口在左边用 1 表示,进水口在上边用 2 表示,进水口在右边用 3 表示,进水口在下边用 4 表示。

还要用 book 数组标记已经用过的水管。

处理当前水管是直型的情况:

if(a[x][y]==5||a[x][y]==6)//如果当前水管是直型的 
{
    if(front==1)//进水口在左边的情况 
	    fun(x,y+1,1);//只能用 5 号这种摆放方式 
	if(front==2)//进水口在上边的情况 
	    fun(x+1,y,2);//只能用 6 号这种摆放方式 
	if(front==3)//进水口在右边的情况 
	    fun(x,y-1,3);//只能用 5 号这种摆放方式 
	if(front==4)//进水口在下边的情况 
        fun(x-1,y,4);//只能用 6 号这种摆放方式 
} 

处理当前水管是弯型的情况:

	if(a[x][y]<=4&&a[x][y]>=1)//如果当前水管是弯型的 
	{
		if(front==1)//进水口在左边的情况 
		{
			fun(x-1,y,4);//3 号摆放 
			fun(x+1,y,2);//2 号摆放 
		}
		if(front==2)//进水口在上边的情况 
		{
			fun(x,y-1,3);//4 号摆放 
			fun(x,y+1,1);//1 号摆放 
		}
		if(front==3)//进水口在右边的情况 
		{
			fun(x+1,y,2);//2 号摆放 
			fun(x-1,y,4);//1 号摆放 
		}
		if(front==4)//进水口在下边的情况 
		{
			fun(x,y+1,1);//2 号摆放 
			fun(x,y-1,3);//3 号摆放 
		}
	}

解决了入水口这一难题,代码就渐渐清晰了。

当然,还要用一个结构体来存储 x 值,y 值,记得出栈和入栈,然后满足条件按出队输出。

代码如下:

// 1 代表从左进 
// 2 代表从上进 
// 3 代表从右进 
// 4 代表从下进 
#include<stdio.h>
struct node 
{
	int x;
	int y;
};
struct node k[2550];
int n,m,a[55][55],book[55][55];
int now;//用 now 来记录结构体中的入栈的个数 
void fun(int x,int y,int front)
{
	//要到达出口,y 仅仅到达 m 是不够的,因为(n,m)处还有水管需要判断
	//所以出口处的水应该流向下一个点,也就是 m+1 
	if(x==n&&y==m+1)
	{
		int i;
		//满足条件,则输出路径 
		for(i=1;i<=now;i++)
		{
			printf("(%d,%d) ",k[i].x,k[i].y);
		}
		return ;
	}
	//排除出界的情况 
	if(x>n||x<1||y>m||y<1)
	return ;
	//排除已经经过的情况 
	if(book[x][y]==1)
	return ;
	//接下来把该点视作经过的点,然后进行标记,入栈 
	book[x][y]=1;
	now++;
	k[now].x=x;
	k[now].y=y;
	//按照水管的形状和入水口的方向进行搜索 
	if(a[x][y]==5||a[x][y]==6)
	{
		if(front==1)
		fun(x,y+1,1);
		if(front==2)
		fun(x+1,y,2);
		if(front==3)
		fun(x,y-1,3);
		if(front==4)
		fun(x-1,y,4);
	}
	if(a[x][y]>=1&&a[x][y]<=4)
	{
		//弯型水管每个不同的入水口方向,会出现两种情况 
		if(front==1)
		{
			fun(x+1,y,2);
			fun(x-1,y,4);
		}
		if(front==2)
		{
			fun(x,y+1,1);
			fun(x,y-1,3);
		}
		if(front==3)
		{
			fun(x+1,y,2);
			fun(x-1,y,4);
		}
		if(front==4)
		{
			fun(x,y+1,1);
			fun(x,y-1,3);
		}
	}
	//地图中为 0 的树木,要出栈 
	now--;
	return ;
}
int main()
{
	int i,j;
	scanf("%d %d",&n,&m);
	//读入地图 
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		scanf("%d",&a[i][j]);
	}
	//开始从(1,1)搜索,并且进水口在左边,用 1 表示 
	fun(1,1,1);
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明里灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值