做DFS(或其他搜索题),我感觉最有趣的地方不是DFS本身而是——“剪枝”。剪枝,顾名思义就是剪去不必要的枝节,也就是避免不必要的搜索过程。有点类似于工程领域的“去噪”,当然这是个人感觉而已。。
HDU 1010这道题是一个典型的迷宫搜索题。给你出口入口,但是你并不是能找到出口就完事了。注意事项:
前面有篇博文《hdu1518》,已经提到了DFS的一般框架就是:循环+递归。
循环,是横着走。递归是竖着走。注意,我这里说的“横”和“竖”并不是对应本题中迷宫的物理意义上的横竖,而是关于 抽象的逻辑意义上的横 和竖。
本题中 的 横 是 某一结点周围的 上、下、左、右 四个方向。
本题中的 竖 就是递归搜索每一结点的 “横”。
终止态:是所走的步数(即题意中的时间)等于给出的时间。若此时还未走到出口,则返回fasle。找到出口则返回true。
本题中我没有使用一个[4][2]的二维数组来保存 上下左右 四个方位的移动。所以看起来代码有些繁琐,但是可读性却高。
bool dfs(int ti,int i,int j) { if(ti==t)//ti是当前步数,t是给出的步数,或者说时间 { if(i==h&&j==w) return true; return false; } if(j<m-1&&!ma[i][j+1]) { ti++;//步数+1 ma[i][j]=true;//迷宫中该点设为已走 if(ti<=t&&dfs(ti,i,j+1)) return true; ti--;//若走此步最终失败了,那么就步数-1 ma[i][j]=false;//再设为该点未走,即回溯的过程 } if(i<n-1&&!ma[i+1][j]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i+1,j)) return true; ti--; ma[i][j]=false; } if(i&&!ma[i-1][j]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i-1,j)) return true; ti--; ma[i][j]=false; } if(j&&!ma[i][j-1]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i,j-1)) return true; ti--; ma[i][j]=false; } return false; }
本题代码简单但是很容易超时,所以要注意剪枝。
首先看理论上的最短路径。
int path = abs(si-h)+abs(sj-w);
(si,ji)、(h,w)分别为起点终点坐标。我们可使用的两种剪枝方案是:
关于第二种方案的解释:
这种方案学名为“奇偶剪枝”。我们已知了最短的步数就是直角三角形的两条直角边,实际上的路径却不一定非要沿着这两条边走的。仔细看看只要是移动方向一直是右、下,那么走到的时候总步数也一定是path的。然而由于墙的存在或许我们不可能一直右、下的走下去。为了避开墙,我们可能会向左走,向上走等等。但为了到达目的地,你在最短步数的基础上,如果向右走了一步,那么某一时候也必须再向左走一步来弥补。所以(t-path)一定要是2的倍数。
if(t<path||(t-path)%2) { cout<<"NO"<<endl; continue; }
看完整代码:
#include <iostream> using namespace std; bool ma[10][10]; int h,w; int n,m,t; bool dfs(int ti,int i,int j) { if(ti==t) { if(i==h&&j==w) return true; return false; } if(j<m-1&&!ma[i][j+1]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i,j+1)) return true; ti--; ma[i][j]=false; } if(i<n-1&&!ma[i+1][j]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i+1,j)) return true; ti--; ma[i][j]=false; } if(i&&!ma[i-1][j]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i-1,j)) return true; ti--; ma[i][j]=false; } if(j&&!ma[i][j-1]) { ti++; ma[i][j]=true; if(ti<=t&&dfs(ti,i,j-1)) return true; ti--; ma[i][j]=false; } return false; } int main() {//freopen("in.txt","r",stdin); while(cin>>n>>m>>t) { if(!n&&!m&&!t) break; memset(ma,false,sizeof(ma)); char c; int si,sj; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { cin>>c; switch(c) { case 'S':si=i;sj=j;ma[i][j]=true;break; case '.':ma[i][j]=false;break; case 'X':ma[i][j]=true;break; case 'D':ma[i][j]=false;h=i,w=j;break; } } int path = abs(si-h)+abs(sj-w); if(t<path||(t-path)%2) { cout<<"NO"<<endl; continue; } if(dfs(0,si,sj)) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }