这道题是一道相当经典的DFS+剪枝。。。做这道题收获比较大,能让你学会几个剪枝技巧。。
一般做这道题的都会把DFS写出来,不难,关键是超时怎么处理,反正在我看了网上的关于剪枝技巧后,当时就有一个感觉,要是别人不跟我说,打死我也想不出来,虽然学起来很简单。。。
下面是别人写的报告:
1 奇偶剪枝:
把矩阵标记成如下形式:
0,1,0,1,0
1,0,1,0,1
0,1,0,1,0
1,0,1,0,1
很明显,如果起点在0 而终点在1 那显然 要经过奇数步才能从起点走到终点,依次类推,奇偶相同的偶数步,奇偶不同的奇数步
在读入数据的时候就可以判断,并且做剪枝,当然做的时候并不要求把整个矩阵0,1刷一遍,读入的时候起点记为(Si,Sj) 终点记为(Di,Dj) 判断总时间与(Di-Xi)+(Dj-Xj)的奇偶性就可以了,是偶数才可以,不是偶数的话直接跳出。。
奇偶剪枝的重要思想是:如果从当前位置还需要偶数(奇数)的时间才能到达目标位置,而当前剩余的时间数
为奇数(偶数),那么即刻退出不再沿着纵深方向继续搜索。略微思考便知:奇偶剪枝的效果是立竿见影的!
temp = (t-time) - abs(si-di) - abs(sj-dj);
if( temp<0 || temp&1 )
continue
2 路径剪枝:D
矩阵的大小是N*M 墙的数量记为wall 如果能走的路的数量 N*M - wall 小于时间T,就是说走完也不能到总的时间的,这显然是错误的,可以直接跳出了
#include<iostream>
using namespace std;
char map[9][9];
int directx[4]={1,0,-1,0};
int directy[4]={0,1,0,-1};
int time,flag,n,m,t;
void DFS(int ex,int ey,int dxx,int dyy)
{
int dx,dy,dtime,temp;
dtime=time;
for(int i=0;i<4;++i)
{
time=dtime;
dx=ex+directx[i];
dy=ey+directy[i];
if(dx>=0 && dx<n && dy>=0 && dy<m)
{
++time;
if((time>t) || ((time==t) && map[dx][dy]!='D') || ((time<t) && map[dx][dy]=='D'))
continue;
if((time==t) && (map[dx][dy]=='D'))
{
flag=1;
return;
}
if(map[dx][dy]=='X')
continue;
temp=(t-time)-abs(dxx-dx)-abs(dyy-dy);
if(temp<0 || (temp%2)!=0)
continue;
map[dx][dy]='X';
DFS(dx,dy,dxx,dyy);
}
if(flag)
return;
}
if(!flag)
{
map[ex][ey]='.';
}
}
int main()
{
int dx,dy,ex,ey,wall;
while(cin>>n>>m>>t)
{
if(n==0 && m==0 && t==0)
break;
memset(map,0,sizeof(map));
time=flag=wall=0;
if(n==0 && m==0 && t==0)
break;
for(int i=0;i<n;++i)
{
for(int j=0;j<m;++j)
{
cin>>map[i][j];
if(map[i][j]=='S')
{
ex=i;
ey=j;
}
else if(map[i][j]=='D')
{
dx=i;
dy=j;
}
else if(map[i][j]=='X')
++wall;
}
}
if((n*m-wall)<=t)
{
cout<<"NO"<<endl;
continue;
}
if((t-abs(dx-ex)-abs(dy-ey))<0 || ((t-abs(dx-ex)-abs(dy-ey))%2!=0))
{
cout<<"NO"<<endl;
continue;
}
map[ex][ey]='X';
DFS(ex,ey,dx,dy);
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}