题目:http://acm.hdu.edu.cn/showproblem.php?pid=1010
题目大意:给出一个迷宫(含起点和终点),要求找出一条路径,这条路径的长度必须为某个规定的长度.
在做本题之前,先学习了一下迷宫问题:http://blog.csdn.net/ten_sory/article/details/66975811
在理解迷宫问题的基础上,再做本题.本题的难点就是剪枝的问题.如果只用一般的DFS+回溯的方法求解这个问题,一定会超时,所以需要一些剪枝技巧.
1.奇偶剪枝:如果当前位置为(x,y),终点为(dx,dy),要求你在T步内从(x,y)走到(dx,dy).剪枝就是确定是否存在在T步内完成(x,y)到(dx,dy)的可能性.(x,y)——>(dx,dy)的最少步骤为abs(dx-x)+abs(dy-y),如下图,就是两点之间的曼哈顿距离.
当然,这个最短距离(设为path1)未必可以走得通,所以存在其他的路径.可以肯定的是,其他任意一条路径(设为path2)的长度一定和path1的长度的奇偶性是相同的.因为,path1和path2的起点和终点在纵轴一定相等,如果path2在纵轴上多走了一步,一定要在纵轴上往回走一步,只有这样,才能到达终点.同理,横轴也是一样.
根据这个结论,可以得出,(x,y)到(dx,dy)的最短路径的长度(步数),一定和所规定的步数的奇偶性相同.如果奇偶性不同,终止这条路径的探测.
2.小小剪枝:如果规定时间为T,而障碍物个数为wall个,则如果n*m-wall=可走的点的个数<=T,那么一定不存在某条可行路径的长度为T,终止即可.
代码如下:
#include
using namespace std;
int flag;
char maze[10][10];
int vis[10][10];
int n,m,T,t;
int sx,sy,dx,dy;
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};//4个方向
int abs(int x){return x>0?x:(-x);}
void dfs(int x,int y,int t)
{
if(x==dx && y==dy && t==T)//到达终点
{
flag = 1;
return;
}
if(x<0 || x>=n || y<0 || y>=m)//出界
return;
/*奇偶剪枝*/
int temp1 = abs(dx-x) + abs(dy-y);
int temp2 = abs(T-t);//剩余时间
int temp = abs(temp1-temp2);
if(temp%2!=0)
return;
//以当前位置(x,y)位置为起点,从上,下,左,右4个方向探测
for(int i=0;i<4;i++)
{
int nx = x + dir[i][0];
int ny = y + dir[i][1];//获取新坐标(nx,ny)
if(0<=nx && nx> maze[i];
for(int j=0;j
递归+回溯法中,如果一个探测return了,只是本次递归结束了,之后回溯,进行下一次递归.所以某次递归的return不代表整个递归函数的return.
本题中,只要某次递归找到一个符合要求的路径,就结束整个函数,而不仅仅是结束此次递归.
所以,本题在回溯之前,需要判断一下是否已经找到了符合要求的路径,即:在dfs(...)之后,立即判断if(flag) return;
本文剪枝策略参考:http://www.cnblogs.com/grubbyskyer/p/3855533.html