思路:
剪枝的思路参考博客:http://www.cnblogs.com/zibuyu/archive/2012/08/17/2644396.html 在其基础之上有所改进
题意可以给抽象成给出一个图,让你求S点到D点之间是否存在一条长度为T的道路。求两地之间的距离用的是dfs,而dfs在这里的关键是找到回溯的条件,就是当到达D点并且剩余步数为0时,则符合题意的要求,由于我们只需要知道这样一条长度为T的路径是否存在,因此当我们发现存在的时候,只需要将一个全局flag给设置为1即可,然后从此之后的所有dfs调用都直接return。
另外关键的问题就是剪枝。当我们的思路走到求两个点的距离时,我们应当“俯视”一下——这两个点S和D,他们之间的距离从整个图上来看有什么性质。在这里有这样的规律:
也就是一开始给我们的两个起始点S和D,如果他们之间距离的最小值的奇偶性和T的奇偶性是不同的,那么在一开始我们就可以判断——“NO”!因为0->0和1->1无论怎么走需要的步数都为偶数步,1->0或0->1无论怎么走需要的步数都为奇数步。利用这点,从一开始我们就可以进行奇偶剪枝。
还有一个并不影响最后AC但是也值得思考的一个剪枝,就是在一开始的时候给定图可走的点数就小于T,那就直接不需要考虑。
代码:
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; char G[10][10];//G[n][m] int dx,dy; int n,m,t; int dir[4][2] = {{0,-1},{0,1},{1,0},{-1,0}}; int flag; void dfs(int cx,int cy,int step) { //now we're at (cx,cy),there're "step" steps left to get to (dx,dy) if(cx<1||cx>n||cy<1||cy>m) return; if(flag) return; if(cx==dx&&cy==dy&&step==0) { flag = 1; return ; } for(int i = 0;i < 4;i++) { int nx = cx+dir[i][0]; int ny = cy+dir[i][1]; if(nx<1||ny<1||nx>n||ny>m) continue;//(1)over-border if(G[nx][ny] == 'X') continue;//(2)can't get to(nx,ny) G[cx][cy] = 'X'; dfs(nx,ny,step-1); if(flag) return; G[cx][cy] = '.'; } } int main() { int sx,sy; int i,j; while(scanf("%d%d%d",&n,&m,&t)&&(n!=0||m!=0||t!=0)) { int wall = 0; for(i=1;i<=n;++i) { scanf("%s",G[i]+1); for(j=1;j<=m;++j) { if(G[i][j]=='S') { sx=i; sy=j; } if(G[i][j]=='D') { dx=i; dy=j; } if(G[i][j]=='X') wall++; } } //cut branches-1 if((abs(sx-dx)+abs(sy-dy))%2 != t%2) { cout<<"NO"<<endl; continue; } //cut branches-2 if(n*m-wall < t) { cout<<"NO"<<endl; continue; } flag = 0; dfs(sx,sy,t); if(flag) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }