一道标准的dfs题,并且需要剪枝,否则会TLE。
奇偶剪枝的原理:两点之间的曼哈顿距离为奇数, 则只能在奇数步内到达,偶数同理。即:假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,则[abs(ex-sx)+abs(ey-sy)]与t同奇偶。
本题用到的另一个剪枝:可走的步数("."的总数+1("D"算一步))< t 时,直接输出“NO”。
此外,我有一个疑问求大佬们解答:当到达终点但时间不对时,直接判false返回还是继续搜索?我认为应该直接return,毕竟一个点只能走一次;但是这样一直WA,所以有没有什么样例可以解释一下?dfs的部分代码如下:
if(maze[xx][yy]=='D')
{
if(tt==t)
{
ans=true;
return;
}
/*else //走过就不能再走,一定false
return;*/
}
如果我把注释取消掉,就会报WA,这是为什么呢?请求大佬解答!!
另外发现以前一直用的vis数组其实可以不用,只要能标记能回溯即可,代码中也附上了相关内容。AC代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int n,m,t;
char maze[10][10];
bool ans=false;
bool vis[10][10];
int d[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
void dfs(int x,int y,int time)
{
if(ans==true)
return;
if(x<0||x>=n||y<0||y>=m||time>t)
return;
for(int i=0;i<4;i++)
{
int xx=x+d[i][0],yy=y+d[i][1],tt=time+1;
if(xx>=0&&xx<n&&yy>=0&&yy<m&&tt<=t)
{
if(vis[xx][yy]||maze[xx][yy]=='X')
continue;
/*if(maze[xx][yy]=='X')
continue; //没有vis数组 */
else if(maze[xx][yy]=='D')
{
if(tt==t)
{
ans=true;
return;
}
/*else //走过就不能再走,一定false(??)
return;*/
}
vis[xx][yy]=true;
dfs(xx,yy,tt);
vis[xx][yy]=false;
/* 不用vis也可以标记(已AC)
char cnt=maze[xx][yy];
maze[xx][yy]='X';
dfs(xx,yy,tt);
maze[xx][yy]=cnt; */
}
}
}
int main()
{
while(scanf("%d%d%d",&n,&m,&t)==3)
{
if(n==0&&m==0&&t==0)
break;
memset(maze,'\0',sizeof(maze));
memset(vis,false,sizeof(vis));
ans=false;
int si=0,sj=0,ei=0,ej=0;//起点终点的坐标
int num=0;//可走的格子总数
for(int i=0;i<n;i++)
{
scanf("%s",maze[i]);
for(int j=0;j<m;j++)
{
if(maze[i][j]=='S')
{
si=i,sj=j;
vis[i][j]=true; //注意标记起点!!!
/*maze[i][j]='X'; //不用vis数组 */
}
if(maze[i][j]=='D')
ei=i,ej=j,num++;
if(maze[i][j]=='.')
num++;
}
}
int Manhattan=abs(ei-si)+abs(ej-sj);//起点终点的曼哈顿距离
if(num<t||(Manhattan%2!=t%2))//剪枝
{
printf("NO\n");
continue;
}
dfs(si,sj,0);
if(ans)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}