【hdoj_1010】Tempter of the Bone(迷宫+剪枝)

题目: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),如下图,就是两点之间的曼哈顿距离.

【hdoj_1010】Tempter of the Bone(迷宫+剪枝)_第1张图片


当然,这个最短距离(设为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

你可能感兴趣的:(算法OJ)