HDU1010:Tempter of the Bone(dfs+剪枝)
http://acm.hdu.edu.cn/showproblem.php?pid=1010 //题目链接
http://ycool.com/post/ymsvd2s//一个很好理解剪枝思想的博客
http://blog.csdn.net/chyshnu/article/details/6171758//一个很好举例的博客
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.
'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.
例题:ZOJ Problem Set - 2110 Tempter of the Bone
题目意思是讲有一只狗要吃骨头,结果进入了一个迷宫陷阱,迷宫里每走过一个地板费时一秒,该地板 就会在下一秒塌陷,所以你不能在该地板上逗留。迷宫里面有一个门,只能在特定的某一秒才能打开,让狗逃出去。现在题目告诉你迷宫的大小和门打开的时间,问你狗可不可以逃出去,可以就输出YES,否则NO。
搜索时要用到的剪枝:
1.如果当前时间即步数(step) >= T 而且还没有找到D点,则剪掉。
2.设当前位置(x, y)到D点(dx, dy)的最短距离为s,到达当前位置(x, y)已经花费时间(步数)step,那么,如果题目要求的时间T - step < s,则剪掉。
3. 对于当前位置(x, y),如果,(T-step-s)是奇数,则剪掉(奇偶剪枝)。
4.如果地图中,可走的点的数目(xnum) < 要求的时间T,则剪掉(路径剪枝)。
题目解析:
通过做这题算是懂剪枝的思想了,要学奇偶剪枝首先要看懂那个01矩阵(很好理解),之后就没什么问题了,
剪完枝后大约100ms就过了,怎么说呢,还是了解思想比较重要。
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> using namespace std; int n,m,t,tx,ty,flag; char map[8][8]; int v[8][8]; int jx[]= {1,-1,0,0}; int jy[]= {0,0,1,-1}; int Distance ( int x, int y ) { return abs(x - tx )+abs( y - ty ); // 当前点(x,y)到终点(tx,ty)的最短距离 } void dfs(int x,int y,int ans) { if(ans>t) return ; if(x==tx&&y==ty&&ans==t) { flag=1; return ; } int dis=t-ans-Distance(x,y); if(dis<0||dis%2) return ;// 剩余步数小于最短距离或者满足奇偶剪枝条件 for(int i=0; i<4; i++) { int xx=x+jx[i]; int yy=y+jy[i]; if(xx>=0&&xx<n&&yy>=0&&yy<m&&v[xx][yy]==0&&map[xx][yy]!='X') { v[xx][yy]=1; dfs(xx,yy,ans+1); if(flag==1) break; v[xx][yy]=0; } } return ; } int main() { int xx,yy,sum; while(scanf("%d%d%d",&n,&m,&t)!=EOF) { flag=sum=0; if(n==0&&m==0&&t==0) break; for(int i=0; i<n; i++) { scanf("%*c%s",map[i]); } for(int i=0; i<n; i++) { for(int j=0; j<m; j++) { v[i][j]=0; if(map[i][j]=='X') { sum++; } else if(map[i][j]=='S') { xx=i; yy=j; v[i][j]=1; } else if(map[i][j]=='D') { tx=i; ty=j; } } } v[xx][yy]=1; if(n*m-sum>t) { dfs(xx,yy,0);// 可通行的点必须大于要求的步数,路径剪枝。 } if(flag==1) printf("YES\n"); else printf("NO\n"); } return 0; }
什么是奇偶剪枝?
把矩阵看成如下形式:
0 1 0 1 0 1
1 0 1 0 1 0
0 1 0 1 0 1
1 0 1 0 1 0
0 1 0 1 0 1
从为 0 的格子走一步,必然走向为 1 的格子 。
从为 1 的格子走一步,必然走向为 0 的格子 。
即:
从 0 走向 1 必然是奇数步,从 0 走向 0 必然是偶数步。
所以当遇到从 0 走向 0 但是要求时间是奇数的或者 从 1 走向 0 但是要求时间是偶数的,都可以直接判断不可达!
比如有一地图:
- S...
- ....
- ....
- ....
- ...D
要求从S点到达D点,此时,从S到D的最短距离为s = abs ( dx - sx ) + abs ( dy - sy )。
如果地图中出现了不能经过的障碍物:
- S..X
- XX.X
- ...X
- .XXX
- ...D
此时的最短距离s' = s + 4,为了绕开障碍,不管偏移几个点,偏移的距离都是最短距离s加上一个偶数距离。
就如同上面说的矩阵,要求你从0走到0,无论你怎么绕,永远都是最短距离(偶数步)加上某个偶数步;要求你从1走到0,永远只能是最短距离(奇数步)加上某个偶数步。
关于奇偶剪枝
首先举个例子,有如下4*4的迷宫,'.'为可走路段,'X'为障碍不可通过
S...
....
....
...D
从S到D的最短距离为两点横坐标差的绝对值+两点纵坐标差的绝对值 = abs(Sx - Dx) + abs(Sy - Dy) = 6,这个应该是显而易见的。
遇到有障碍的时候呢
S.XX
X.XX
...X
...D
你会发现不管你怎么绕路,最后从S到达D的距离都是最短距离+一个偶数,这个是可以证明的
而我们知道:
奇数 + 偶数 = 奇数
偶数 + 偶数 = 偶数
因此不管有多少障碍,不管绕多少路,只要能到达目的地,走过的距离必然是跟最短距离的奇偶性是一致的。
所以如果我们知道从S到D的最短距离为奇数,那么当且仅当给定的步数T为奇数时,才有可能走到。如果给定的T的奇偶性与最短距离的奇偶性不一致,那么我们就可以直接判定这条路线永远不可达了。