HDU 1010 - Tempter of the Bone(DFS剪枝)
Tempter of the Bone
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 176647 Accepted Submission(s): 46964
Problem Description
The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze began to shake, and the doggie could feel the ground sinking. He realized that the bone was a trap, and he tried desperately to get out of this maze.
The maze was a rectangle with sizes N by M. There was a door in the maze. At the beginning, the door was closed and it would open at the T-th second for a short period of time (less than 1 second). Therefore the doggie had to arrive at the door on exactly the T-th second. In every second, he could move one block to one of the upper, lower, left and right neighboring blocks. Once he entered a block, the ground of this block would start to sink and disappear in the next second. He could not stay at one block for more than one second, nor could he move into a visited block. Can the poor doggie survive? Please help him.
Input
The input consists of multiple test cases. The first line of each test case contains three integers N, M, and T (1 < N, M < 7; 0 < T < 50), which denote the sizes of the maze and the time at which the door will open, respectively. The next N lines give the maze layout, with each line containing M characters. A character is one of the following:
‘X’: a block of wall, which the doggie cannot enter;
‘S’: the start point of the doggie;
‘D’: the Door; or
‘.’: an empty block.
The input is terminated with three 0’s. This test case is not to be processed.
Output
For each test case, print in one line “YES” if the doggie can survive, or “NO” otherwise.
Sample Input
4 4 5
S.X.
…X.
…XD
…
3 4 5
S.X.
…X.
…D
0 0 0
Sample Output
NO
YES
小明做了一个很久很久的梦,醒来后他竟发现自己和朋友在一个摇摇欲坠的大棋盘上,他们必须得想尽一切办法逃离这里。
经过长时间的打探,小明发现,自己所在的棋盘格子上有个机关,上面写着“你只有一次机会,出发后t秒大门会为你敞开”,而他自己所在的棋盘是大小为 N*M 的长方形,他可以向上下左右四个方向移动(不可走有障碍点)。棋盘中有一扇门。根据机关的提示,小明顿时明白了,他和朋友必须在第 t 秒到门口。而这一切,没有回头路!因为一旦他移动了,他刚才所在的点就会消失,并且他不能在一个点上停留超过一秒,不然格子会爆炸。大逃亡开始了,请问小明和朋友能安全的逃出这奇怪的棋盘吗?
Input
输入多组测试数据。每个测试用例的第一行包含三个整数 N、M 和 T ( 1 < N , M < 7 ; 0 < T < 50 ),分别表示棋盘的大小和门打开的时间。接下来的N行给出棋盘布局,每一行包含M个字符。其中
“.”: 无障碍点
“X”: 障碍点
“S”: 起点
“D”: 门
输入以 3 个 0 结束。这个测试用例不需要处理。
输入数据中的空格有些问题,请不要使用getchar(),如果一定要用可以选择scanf("%s",) 自动忽略空格
Output
对于每组样例输出一行。
如果小明能够安全逃出,输出 “YES” ,否则输出 “NO”。
题目大意:
给出一个地图,’ . ‘代表空地,’ X '代表障碍,S代表起点,D代表出口,询问是否能恰好T步走到D。
解题思路:
DFS奇偶剪枝问题,刚开始没有好好读题用BFS做WA了几发,然后重新读了下题,发现要恰好T步到达出口D,而且路一旦走过就会被销毁,也就是说不能上上下下左左右右BABA一样刷步数,而且要DFS的话注意下时间问题,不剪枝就会TLE,这里说一下几个要剪枝的地方:
- 步数与起点到终点最小距离的奇偶性,这里要注意一个点,求一下起点到终点的曼哈顿距离,abs(ex-sx)+abs(ey-sy)得到起点到终点的最小步数,我们可以发现,要先T步恰好到达,T和两点的曼哈顿距离奇偶性必须相同,如果不相同则一定不能走到。
- DFS的过程中,如果当前步数step>t,则一定走不到,return false 剪掉就可以了。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
char mp[10][10];
bool book[10][10];
int n,m,t;
int dir[][2]={0,1,0,-1,1,0,-1,0};
bool dfs(int x,int y,int s)
{
if(s>t)//剪掉
return false;
if(mp[x][y]=='D'&&s==t)
return true;
for(int k=0;k<4;k++)
{
int nx=x+dir[k][0];
int ny=y+dir[k][1];
if(nx<0||nx>n-1||ny<0||ny>m-1)
continue;
if(!book[nx][ny]&&mp[nx][ny]!='X')
{
book[nx][ny]=true;
if(dfs(nx,ny,s+1))
return true;
book[nx][ny]=false;
}
}
return false;
}
int main()
{
while(cin>>n>>m>>t)
{
if(!n&&!m&&!t)
break;
for(int i=0;i<n;i++)
cin>>mp[i];
int sx,sy,ex,ey;
memset(book,false,sizeof book);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
if(mp[i][j]=='S')
{
sx=i,sy=j;
book[i][j]=true;//这个点必须注意!WA了无数发就是找不出错
}
if(mp[i][j]=='D')
ex=i,ey=j;
}
bool flag;
int s=abs(ex-sx)+abs(ey-sy);
if(s%2!=t%2)//奇偶剪枝
flag=false;
else
flag=dfs(sx,sy,0);
cout<<(flag?"YES":"NO")<<endl;
}
return 0;
}
关于注释:起点必须标记已经走过,不标记一定WA,因为走过以后就不能走了,看一个样例:
3 3 3
X . X
XSX
XDX
关于这个样例,很明显是不对的,但是如果不标记的话就会输出OK,因为S走到 ‘ . ’ 时,继续搜发现S点还没有被标记,就又回到了S,再到D,发现恰好3步,所以这个地方不标记是绝对不行的 :(