题目:HDU-1010
Tempter of the Bone
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1010
Tempter of the Bone
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 97086 Accepted Submission(s): 26346
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
这道题目说实话刚开始看我是不会的,写完dfs就傻了,实在不知道怎么把“时间”(也就是步数)的计算加进去,而且这道题目本身问的是能否在指定时间点逃生,也就是说不一定是最短路可能还是最长路也可能是中间徘徊之后才满足逃出条件,而且就算少于最长时间,多于最短时间也不一定能够恰好在指定时间到终点,但是稍微手动跑两次就发现是隔一个可以跑,不过就算发现了这些,还是没什么卵用,依然是束手无策。
废话就不再说了,因为是第一道dfs的题目,所以会从无到有说一下dfs,之后遇到的dfs题目就不细讲了,首先说一下dfs(深度优先搜索)是一种搜索的算法,在ACM算法里面也是赫赫有名的一种,当然也是基础必须掌握的一种算法。它其实是一种基于树结构来进行搜索的,例如二叉树,dfs就是先搜索最底层的,然后依次向上搜索,而这种搜索大多数是基于递归来实现分支搜索的。而我们使用dfs解题也正是将题目转化为树形结构(仅仅是思想上,形式上还是原来的结构),其中最常见的就是迷宫搜索,给定起点,给定终点,给定障碍,我们就可以以从起点出发,利用递归,依次向上下左右搜索出口,然后再把上下左右每一个点当成初始点在进行搜索,这就类似于一种“树形结构”了,直到遇到障碍、遇到边界,那么搜索就停止了,遍历整张图以后就完成了搜索,搜索其实是一种思想,由此展开可以产生很多方面的问题,比如这一道题,所以需要具体问题具体对待,OK,来看这一道题。
这个题目要求上面已经说过了,它的特别要求是:遇到墙走不通,走过的路不能再走,需要恰好在指定的时间走到终点。
那么首先我们在输入图之后就要找到起点,从起点出发开始dfs,注意这里还有一个时间,所以我们搜索的变量传递一栏里面还需要加上一个时间,然后开始dfs,这中间当然会出现很多问题,比如,我刚开始写的时候没有在每个dfs之后把它的状态重新变为没有走过的,那么这个时候问题就产生了:我这样搜索的结果只相当于一种路径,只有伪最短路的搜索结果(也不一定就是最短路),一旦我第一步向右走成功了,那么第一步就被标记为“走过”了,那么之后的方法就不能再走了,也就是说一种走法对整张图造成了影响,这样明显是不正确的,所以在dfs的过程中一定要注意单次路径是否对其它路径产生了影响,如果不注意这点最后很可能产生似是而非的结果,错都不知道哪里错了。在解决完这个,将所有的路径都遍历之后,我们只需要在每次递归的时候都把附带的time++,那么time的值就是这个路径所花费的时间,我们只需要另外加一个全局变量bool来表示最终是否能逃脱的状态,每一次都判断是否到了终点而且时间是否是要求的结果(因为我们已经把所有的路径都搜索了一遍,所以如果有可以逃生的路径一定会满足条件的,不清楚的话可以跟着dfs模拟两次),如果是就把bool赋为true,最后加一个判断就行了。
这个弄好之后貌似可以了,样例是可以过了,但是这道题却没有结束,这样的代码交上去是绝对超时的,因为递归的路径太多了所以本来就很容易超时。所以我们就需要进行剪枝,剪枝就是“剪去”不必要的搜索路径以避免不必要的时间浪费。当然以我的智商是无论如何也想不出来奇偶剪枝这种东西的,也是看了大神的博客才知道,但是我们自己可以想到的还有一些小地方是可以剪的:
1.如果起点到终点的绝对最短路径(无视障碍)步数(也就是时间)都大于要求时间t的话,那也没必要走了,直接NO就行。
2.如果所剩下的所有可以走的步数(除了墙以外)都小于要求时间t的话,那也没必要走了,NO。
3.如果我们已经找到可以逃出的路径的话,那也没必要再dfs了,所以我们在dfs最开始加一个判断如果满足可以逃出ans=1;直接return减少不必要的寻找。
之后就是神奇的奇偶剪枝了:
,
如果我们把一张图看成这个样子的话,我们很容易发现一个规律,从0走到0的时候用的步数是偶数步,0走到1时用的步数是奇数步,而这道题目要求的是恰好在一个时间点到达终点,也就是说知道起点坐标、终点坐标,我们就可以判断出最终的答案是奇数还是偶数,如果t不是我们计算出来的偶数或者奇数,那就可以直接NO掉,不用dfs,这个优化对于随机产生的测试数据来讲作用是显而易见的(当然对于特殊设计的数据来讲意义可能不是很明显),而加上这个判断只需要在输入时稍加判断就行,何乐而不为。
由此可见,搜索题目考验的绝对不仅仅是搜索,更重要的是剪枝优化,能否想到优化,怎么样能够巧妙地根据不同一幕进行不同的优化,怎么优化就看题目啦。上代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=9;
char map[maxn][maxn];
int visit[maxn][maxn];
bool ans=0;
int n,m,t;
int abs(int x){
return x>=0?x:-x;
}
void dfs(int x,int y,int time){
if(ans) return;
if(x-1>=0){
if(visit[x-1][y]==3 && time==t){
ans=1;
return;
}
if(visit[x-1][y]==0){
visit[x-1][y]=1;
dfs(x-1,y,time+1);
visit[x-1][y]=0;
}
}
if(x+1<n){
if(visit[x+1][y]==3 &&time==t){
ans=1;
return;
}
if(visit[x+1][y]==0){
visit[x+1][y]=1;
dfs(x+1,y,time+1);
visit[x+1][y]=0;
}
}
if(y-1>=0){
if(visit[x][y-1]==3 && time==t){
ans=1;
return;
}
if(visit[x][y-1]==0){
visit[x][y-1]=1;
dfs(x,y-1,time+1);
visit[x][y-1]=0;
}
}
if(y+1<m){
if(visit[x][y+1]==3 && time==t){
ans=1;
return;
}
if(visit[x][y+1]==0) {
visit[x][y+1]=1;
dfs(x,y+1,time+1);
visit[x][y+1]=0;
}
}
return;
}
int main(){
while(cin>>n>>m>>t){
if(n==0 && m==0 && t==0) break;
int x,y,x2,y2,k1,k2;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
cin>>map[i][j];
if(map[i][j]=='X')
visit[i][j]=-1;
else if(map[i][j]=='S'){
visit[i][j]=2;
k1=(i+j)%2;
x=i;y=j;
}
else if(map[i][j]=='D'){
visit[i][j]=3;
x2=i;y2=j;
k2=(i+j)%2;
}
else
visit[i][j]=0;
}
k1=(k1+k2)%2;
if((k1==0 && t%2==1)||(k1==1 && t%2==0)||(abs(x2-x)+abs(y2-y)>t)){
cout<<"NO"<<endl;
continue;
}
ans=0;
dfs(x,y,1);
if(ans)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
加油,好好学习,天天向上。