您的位置:首页 > 其它

HDU1728 逃离迷宫 DFS

2014-08-24 12:06 253 查看
逃离迷宫
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Submit Status

Description

  给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,gloria不能走到迷宫外面去。令人头痛的是,gloria是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,gloria所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。gloria能从一个位置走到另外一个位置吗?

Input

  第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,

  第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x 1, y 1, x 2, y 2 (1 ≤ k ≤ 10, 1 ≤ x 1, x 2 ≤ n, 1 ≤ y 1,
y 2 ≤ m),其中k表示gloria最多能转的弯数,(x 1, y 1), (x 2, y 2)表示两个位置,其中x 1,x 2对应列,y 1, y2对应行。

Output

  每组测试数据对应为一行,若gloria能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。

Sample Input

2
5 5
...**
*.**.
.....
.....
*....
1 1 1 1 3
5 5
...**
*.**.
.....
.....
*....
2 1 1 1 3


Sample Output

no
yes


这个题卡在不知道怎么判是否转弯,不过看了大神的解题就懂了,程序里的(dir!=-1 && i!=dir)就是用来判断是否转弯了。

本题我是用的dfs,找到终点时并不晕就返回真,程序并没有遍历整个图。并且在走重时会根据在重点的转弯值turn判断以选优,

这在程序中已实现,并作为剪枝,注意本题剪枝很重要,不然会超时的!!

下面来解释一下bfs()中if()剪枝中为什么相等的情况不能剪掉(先看代码去):

如图:


很明显,如果k=1的话,那么途中红线是不符合题意的,我们来谈论最中心的那个点(下面我们叫它中心点):

走红线时,在中心点的turn值是1,当然要去终点的到还需要再转一次,就不符合题意,一次放弃这条路线,但是中心点的turn就此存在了,并且是1.

当再走蓝线时,走到该点时turn值也是1,此时人没晕,一直走是可以走到终点的,所以这种情况是应该输出“yes”的。

但如果你把相等的情况剪掉了,那么就是相当于把蓝线这种可能给否定了,那自然就错了。

造成这种情况的原因其实很简单,因为不同路线的到达相同的点时,因为来的时候方向不同,要是再朝相同的方向(即终点的方向)走时,有的需要转一次;有的则不需要,接着原来的方向走就行。

代码如下:

#include <stdio.h>
#include <string.h>
#include <math.h>
#define Max 999999
#include <algorithm>
using namespace std;

struct point
{
int x,y;
}s[20000];

char map[110][110];
int turn[110][110];
int n,m,x2,y2,ok,k;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};

void dfs(int x,int y,int dir)
{
int i,xx,yy;
if(x==x2 && y==y2 && turn[x][y]<=k)		//成功的时候返回
{
ok=1;
return ;
}
if(turn[x][y]>k)			//大于k时已晕,不行
return ;

if(x!=x2 && y!=y2 && turn[x][y]==k)			//如果(x,y)和终点(x2,y2),既不在同一行也不在同一列,那么要想到终点至少需要转一次,但现在已经转够k次了,故不行
return ;
for(i=0;i<4;i++)
{
xx=x+dx[i];
yy=y+dy[i];
if(xx<=0 || yy<=0 || xx>m || yy>n || map[xx][yy]=='*')
continue;
//注意!!!下面这行中的turn[xx][yy]是表示(xx,yy)已经由别的路线走过了,并记录了turn[xx][yy],现在需要比较这次走到(xx,yy)和由别的路线走到
//(xx,yy)时,两个的turn值,如果上次的比这次的小,说明这次不行,故要continue;
if(turn[xx][yy]<turn[x][y])		//这里相等的情况不能剪掉,原因开头已解释
continue;
//下面这条if和上面的差不多,目的是:如果从(x,y)走一步到(xx,yy)需要转一次话,并且转过之后turn[x][y]+1依然比turn[xx][yy]大的话,也不符合
if(dir!=-1 && i!=dir && turn[xx][yy]<turn[x][y]+1)
continue;			//这两个if语句剪枝很重要,没有的话就超时
if(dir!=-1 && i!=dir)
turn[xx][yy]=turn[x][y]+1;
else
turn[xx][yy]=turn[x][y];
map[xx][yy]='*';		//如果这里能走,就把这里变成不能走,然后再从这里开始递归,其实就是起到vis[][] 的作用,会用vis的话就不用追究了
dfs(xx,yy,i);
map[xx][yy]='.';		//这里再变成'.',是为了不影响其他的递归过程,因为其他路线可能还要从这里过
if(ok)
return ;
}

}

int main()
{
int i,j,t,x1,y1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
scanf(" %c",&map[i][j]);
scanf("%d%d%d%d%d",&k,&y1,&x1,&y2,&x2);		//注意,这里是先接y,再接x,我被坑了好长时间
memset(turn,Max,sizeof(turn));			//因为在dfs()中剪枝要去最小的转弯次数,所以turn要初始化成最大
ok=0;
turn[x1][y1]=0;
dfs(x1,y1,-1);
if(ok)
printf("yes\n");
else
printf("no\n");
}
return 0;
}


另外需要注意的就是,scanf时,%c前面加个空格,即scanf(" %c",....),不然不行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: