题目链接。题目大意:给定迷宫的地图,起点S终点D和限制,D是一扇门,会在T时刻内打开,只有当D打开并且dog到达该点才能成功的逃脱,否则无法逃脱。给出相关的参数,判断dog是否可以逃脱。
看完之后发现这显然是一个dfs搜索的题目,我们当前点进行四个方向的搜索,判断步数和时间的关系即可。但是会发现程序超时,那么说明一个问题,我们的减支的力度不够,要从别的方面思考该如何进行减支。看到这个题和一般的题有一点小小的区别,一般的迷宫搜索都是在规定时间内出去即可,但是这里需要在准确的时间到达,因为题目中要求每一个点只能到达一次并且不能停留。所以在这里最短路不一定是最正确的选择。我们来思考dfs的减支
(1)T时刻内到达出口,递归结束
(2)T时刻内没有达到出口,递归也要结束
(3)小于T时刻内达到出口,这个时候没用,因为门没有开,所以也没用。
使用这三个减支的规则来减支,提交发现依然是超时。这个时候就开始怀疑人生了,难道这个题不是dfs?不会吧,显然是的啊。说明我们还有一些地方的减支没有考虑到。继续向下分析:这个题目很大的特点就是在迷宫中需要使用恰好T秒,不能多也不能少。那么其实我们可以发现一些二东西。假设出口D的坐标是在x,y当前的坐标点是在px,py.那么最少的时间肯定是两点之间的哈密顿距离。如果这时的时间已经不足,那么显然也是不能到达的,因为最短的时间已经不允许出去。OK,这个时候我们其实有得到了一个很重要的减支,其实我们在每一层的dfs都加一个这样的判断,那么就可以起到很大的作用。加上这个减支之后依然是超时,what?这nm还超,行吧,终极大招,奇偶减支。结合代码来说明:
char map[10][10]; //7*7
int book[10][10];
int p, q, n, m, t, flag;
int next[4][2] = { {0,1}, {1,0}, {0,-1}, {-1,0} }; //右下左上
void dfs(int x, int y, int step)
{
//走的步数即等于时间
int i, tx, ty, temp;
if (flag) return; //有一个方案成功及时结束
if (x == p && y == q) { //到达门
if (step == t) flag = 1;
return;
}
if (step >= t) return; //!!!剪枝 超时或没走到
temp = t - step - abs(p - x) - abs(q - y);//这个
if (temp < 0 || temp & 1) return;
//剪枝:如果剩余的步数不足以走到出口,且必须是偶数,偶-偶=偶,奇-奇=偶
for (i = 0; i < 4; i++)
{ //尝试4个方向
tx = x + next[i][0];
ty = y + next[i][1]; //下一步的坐标
if (tx<1 || tx>n || ty<1 || ty>m) //是否越界
continue;
if (book[tx][ty] == 0 && map[tx][ty] != 'X')
{
book[tx][ty] = 1; //标记已走过
dfs(tx, ty, step + 1);
if (flag) return; //有一个方案成功及时结束
book[tx][ty] = 0; //回溯
}
}
return;
}
int main() {
int i, j, startx, starty, wall;
while (scanf("%d%d%d", &n, &m, &t)) {
getchar();
if (n == 0 && m == 0 && t == 0) break;
flag = wall = 0;
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++)
{
map[i][j] = getchar();
if (map[i][j] == 'S')
{ //初始位置
startx = i;
starty = j;
}
else if (map[i][j] == 'D')
{ //门的位置
p = i;
q = j;
}
else if (map[i][j] == 'X')
wall++;
}
getchar();
}
if (t > n*m - wall) printf("NO\n");
//abs(p-startx)+abs(q-starty)为到达终点的最少步数
else if (abs(p - startx) + abs(q - starty) >= t && (startx + starty + p + q + t) % 2 == 1) printf("NO\n");
//偶数点到偶数点(坐标xy之和)需要走偶数次,奇数到奇数也要走偶数次。
else
{
memset(book, 0, sizeof(book)); //清零标记
book[startx][starty] = 1; //标记初始的占位
dfs(startx, starty, 0);
if (flag) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
/*
这里回溯的原因:在搜索整个迷宫的时候,对于当前点:进行四个方向的搜索,dx=x+nex[i][j].假设这个点已经完成搜索
那么需要转移搜索中心,所以这里需要回溯
*/