题目大意:给出三个数,n,m,t,前两个数代表迷宫的大小,第三个数代表从迷宫入口到出口的时间,要求恰好在这个时间到达迷宫出口。
思路分析:因为不是求最短到达迷宫出口的时间,而是要求在固定的时间到达迷宫出口,也就是到达迷宫出口的路径不一定是最短的,所以不能用bfs,而是改用dfs,但是要注意剪枝,我就因为没有进行剪枝而超时了一次,这里使用的是奇偶剪枝,什么意思呢?
奇偶剪枝:
s | ||||
| | ||||
| | ||||
| | ||||
+ | — | — | — | e |
我们假设s是起点,e是终点,‘|’代表竖着走,‘+’代表拐弯,‘-’代表横着走,图中就是s->e的最短路径,即min_step=abs(ex-sx)+abs(ey-sy)=8,在这道题中是偶数;
奇偶剪枝的核心思想就是我们所走的任何一条路径长度一定和min_step的奇偶性是一样的,可以自己画图试试看,给定时间t,也就相当于规定了所走路径的步数,也就是固定了路径,那么如果这条路径的奇偶性和min_step的奇偶是相同的,就说明这条路径是存在的,否则则说明这条路径是不存在的。
这道题我一共用奇偶剪枝两次:
第一次是在输入之后调用dfs之前,我们可以判断一下给定的时间t和最短路径长度的奇偶是否相同,如果不同,就说明我们没有办法在t时间到达出口,;
第二次是在进行dfs的过程中,我们走到任意一点(设为ss)
,把这个点当做起点,终点不变,此时的规定时间(设为tt)就相当于总规定时间减去到达这个点所用的时间,此时也就是判断从ss到终点存不存在一条步数为tt的路径,也就判断在ss到终点的最短距离和tt的奇偶性是否相同,如果相同,就说明存在步数为tt的这条路径,可以以这个点继续走下去,如果不相同,则说明这条路径不存在,不能以这个点当做起点走下去,无论哪个方向,直接返回这个点的上一个结点,这样也就直接减少了从这个点继续走下去的时间,减少大量不存在路径的判断,也就是我们奇偶剪枝的意义所在。
注意:每次走一个格,所以时间也就是路径的长度。
代码实现:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char map[10][10];
int visit[10][10];
int n,m,t,flag,end_x,end_y,xi,yi;
int d[4][2]={-1,0,1,0,0,-1,0,1};
void dfs(int x,int y,int time){
if(map[x][y]=='X'||visit[x][y]==1||x<0||x>=n||y<0||y>=m)
return;
if((abs(x-end_x)+abs(y-end_y))%2!=(t-time)%2) return;//第二次奇偶剪枝
if(time>t) return;
if(map[x][y]=='D'&&time==t){
flag=1;
return;
}
if(flag) return;
visit[x][y]=1;
for(int i=0;i<4;i++){
dfs(x+d[i][0],y+d[i][1],time+1);
}
visit[x][y]=0;
}
int main(){
while(~scanf("%d%d%d",&n,&m,&t),(n||m||t)){
memset(visit,0,sizeof(visit));
flag=0;
for(int i=0;i<n;i++){
scanf("%s",map[i]);
for(int j=0;j<m;j++){
if(map[i][j]=='S'){
xi=i;
yi=j;
}
if(map[i][j]=='D'){
end_x=i;
end_y=j;
}
}
}
if(t%2!=(abs(end_x-xi)+abs(end_y-yi))%2||t<(abs(end_x-xi)+abs(end_y-yi))){//第一次奇偶剪枝
printf("NO\n");
continue;
}
dfs(xi,yi,0);
if(flag) printf("YES\n");
else printf("NO\n");
}
}