题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1072
方法:DFS,记忆化搜索
思路:很标准的迷宫题,考虑用深搜,但是这个题有如下特点,一个是可以重复行走,第二个就是可以重设定爆炸时间,这其实意味着要在原来DFS的基础上要做一些必要的修改,而且有一些细节也要注意,相应要注意的地方在代码注释里已经体现,但是关于剪枝的方法还是不甚明白,现在先记录下来,还希望各路大神看到的话能说一下,那个剪枝的方法究竟是什么意思?
难点:记忆化搜索,DFS
#include <cstdio>
#include <cmath>
#include <climits>
using namespace std;
const int MAX = 10;
int maze[MAX][MAX],s[MAX][MAX],time[MAX][MAX];
int go[4][2] = {1,0,-1,0,0,1,0,-1};
int n,m,minum;
void dfs(int x,int y,int setTime,int step)
{
int tx,ty;
if(x>=m||x<0||y>=n||y<0) return;//越界判断,这里注意输入maze的时候二维数组下标的设置
if(setTime <= 0||step>=minum) return;//如果调用时已经过了爆炸时间或者当前步数已经超过了已经搜索到的最小步数,则跳过
if(maze[x][y] == 0) return;//判断是否为墙,是则跳过
if(maze[x][y] == 3)
{
if(minum > step)
minum = step;
return;//注意这个return的位置,它是指达到终点后跳出,而不是最小值判定跳出
}
if(maze[x][y] == 4)
setTime = 6;//重置爆炸时间
//下面的这个剪枝很重要,不剪就会超时
//从当前点x,y走到下一个可能点的距离大于从其他途径到tx,ty的距离,且到tx,ty点时的剩余时间大于由x,y点到tx,ty点后的剩余时间,就跳过
//这是因为结点可重复访问所以本身没标记,那么当上述条件满足时,由tx,ty开始的最优解已经求过(存在或者不存在),所以不需要再重复求了。
if(step >= s[x][y] && time[x][y] >= setTime)return;
s[x][y] = step;
time[x][y] = setTime;
for(int i = 0;i < 4;i++)
{
tx = x+go[i][0];
ty = y+go[i][1];
dfs(tx,ty,setTime-1,step+1);
}
}
int main()
{
int t;
int sx,sy;
while(~scanf("%d",&t))
{
while(t--)
{
scanf("%d %d",&m,&n);
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
time[i][j] = 0;
s[i][j] = INT_MAX-3;//这里置一个大数,表示到i,j的步数无限大
scanf("%d",&maze[i][j]);
if(maze[i][j] == 2)
{
sx = i;
sy = j;
}
}
}
minum = INT_MAX;//这里用到了climits的头文件,规定一个int最大值作为无穷大
dfs(sx,sy,6,0);
if(minum == INT_MAX)// if(setTime <= 0||step>=minum) return,因为这条语句,如果不能规定时间内走出迷宫,minum就不会被改变
printf("-1\n");
else
printf("%d\n",minum);
}
}
}