这是一个用深搜解题的例子。
【题目描述】
最近小哈又迷上一个叫水管工的游戏。游戏的大致规则是这样的。一块矩形的土地上被分为 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;
}