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.
这道题主要讲述的是一个狗掉进一个迷宫,要我们在规定的步数,并且不能走已经走过的路到达终点,在这道题目中,主要要了解剪枝,一开始没用剪枝,我没有在规定时间内完成,用了剪枝之后时间变成了600多ms(不了解那些用时不过100ms的大神还用了什么进行优化),下面我就主要讲解一下我对这道题目的看法。
先给上代码:
#include
using namespace std;
int time;
int n;
int m;
char migong[128][128];
int startx;
int staryy;
int endx;
int endy;
int lujin[128][128];
bool dfs(int x, int y, int second)
{
if (migong[x][y] == 'X'
|| lujin[x][y] == 1
|| (time - second) < (endx + endy - x - y)
||((time-second)-(endx+endy-x-y))%2==1)
return false;
else
{
if (migong[x][y] == 'D')
{
if (second == time)
return true;
else
return false;
}
lujin[x][y] = 1;
if (x + 1 < n )
{
if (dfs(x + 1, y, second + 1))
{
return true;
}
}
if (y + 1 < m)
{
if (dfs(x, y + 1, second + 1))
{
return true;
}
}
if (x - 1 >= 0)
{
if (dfs(x - 1, y, second+1))
{
return true;
}
}
if (y - 1 >=0)
{
if (dfs(x, y-1, second+1))
{
return true;
}
}
lujin[x][y] = 0;
}
return false;
}
int main()
{
while (cin >> n >> m >> time,n||m||time)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
lujin[i][j] = 0;
}
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> migong[i][j];
if (migong[i][j] == 'S')
{
startx = i;
staryy = j;
}
if (migong[i][j] == 'D')
{
endx = i;
endy = j;
}
}
}
if (dfs(startx, staryy, 0))
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
首先是dfs算法,我们可以先针对当前结点来进行思考,我们这个节点可能是墙,也可能这个节点到目的点的步数大于我们剩余的步数,还有可能这个结点是原来走过的节点,还有可能这个节点到目的节点的最短数目和我们剩余的步数的差不是偶数,即不满足剪枝条件,只要这些条件满足一个的话,我们就可以return false返回(这些体现在dfs的第一个if的判断中),剪枝详细的可以这篇文章。
http://m.blog.csdn.net/deng_hui_long/article/details/9903927
如果这些都满足的话,我们这个时候先对这个节点进行路径保存,(在我提供的代码中可以仔细考虑这个问题,这个在最后在讨论吧!)然后再对这个节点的上下左右进行dfs深度优先遍历,当然在我们深度优先遍历,只要有一个返回true,那么我们就可以在这个节点返回true,因为有一个已经找到了一条路。如果我们所有dfs遍历都没有返回true,那么我们需要把这个节点的路径先清除(路径等等再详细解释为什么要清除!),然后再对上一层返回false!
对于这份代码,dfs其实是一个很容易想到的方法,但是一个只有dfs的算法的提交上去就会发现超时,所以我们需要对dfs进行优化,我选择了在dfs上加上两种优化,就是前面提到的节点到目的节点的最短步数及剪枝经行优化。
我们最后在讨论一下我前面说的路径保存问题,因为对于dfs中的深度优先遍历,我们需要针对每一条路径进行自己路径的访问,不能对于从左边走的点的路径,访问从右边的点的路径,所以一开始对于这个问题,我第一反应是不能创建全局对象(其实是可以的),这样的话路径会乱,对于左边方向的点还能访问查看右边方向的点路劲,所以一开始在dfs上加了一个形参保存路径,然后我发现int **的形参就然调试的时候是个一维数组,这一点我也不太懂为什么。后面我从网上看到一种利用全局变量来保存路径,这种方法的想法(我的个人理解)是对于这个结点后面的所有深度遍历,那么这个结点一定是在他们的路径里面,当这个节点不能返回true的时候那么他就不会在出现在其他的路径中(在那个步数的时候),所以再把它的路径清除,再返回false。
以上就是我对这个问题的理解。