Description
Input
Output
Sample Input
2 8 8 ######## #......# #.####.# #.####.# #.####.# #.####.# #...#..# #S#E#### 9 5 ######### #.#.#.#.# S.......E #.#.#.#.# #########
Sample Output
37 5 5 17 17 9
题目大意:给你一张图,“#”代表 墙, “.”表示路, “S”和“E”分别代表起点,出口。有三种走法:左转优先,右转优先,正常走法。让你求出分别三种走法从起点走到出口所需的最小步数(其中,起点“S” 算是第一步)。显然,求正常走法的最短路径时,直接用BFS就行啦, 但是此题难点在于如何应用DFS求出 左转优先和右转优先 的最短路径 ,而DFS中的难点在于每次搜索时的前进方向应如何确定?? 下面以 左转优先为例,向大家讲述。
先看一张图 :
北(1)
西(0) 当前位置 东(2)
南(3)
如果路人甲 从 东(编号为2)来 到“当前位置 ”,那么此时 他 的前方是 西(编号为0) 并记录为 d = 0,他的 左手边是 南(编号 为 3),因为左转优先,于是 在走下一步时,他的 方向选择顺序依次是 3,0,1,2, 如果3能走,那么他下次在“当前位置”时 面朝的方向依旧是 3,同理,如果3走不通 ,2 能走通,那么路人甲 下次 在“当前位置”时 面朝的方向依旧是 2, 这样就方便记录啦!运用DFS 的关键 就在于此 !!!
具体讲解请看程序:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<cmath> using namespace std; struct point { int x; int y; int dis; }; queue<point>q; int ldis ; // 定义左转优先的步数 int rdis ; // 定义右转优先的步数 char map[46][46]; int vis[46][46]; // 标记数组 ,用于BFS int X[4]= {0,-1,0,1}; // 定义前进方向,注意此处 方向 不是 随便定义的, //与上述解释中的各个方向的编号有关, int Y[4]= {-1,0,1,0}; point start,end; int n ,m; int di ,dj; // 终点坐标 int bfs(); int cango(int i,int j); // bfs 中能够前进的判断条件 int cangoDfs(int i,int j); // 两个dfs中能够前进的判断条件 void leftdfs(int si,int sj,int d); // 左转优先的dfs,其中,si,sj是起点坐标,d是当前位置面朝的方向~~ void rightdfs(int si,int sj,int d); // 右转优先的dfs int pan ; // 判断变量 ,dfs 中要用 ~ int main() { int t; scanf("%d",&t); while (t--) { scanf("%d%d",&m,&n); int si,sj,d; int i,j; for(i=1; i<=n; i++) { for(j=1; j<=m; j++) { cin>>map[i][j]; if(map[i][j]=='S') { si = i; sj = j; start.x = i; start.y = j; if(map[si][sj-1]!='#') // 判断路人甲在起点”S“时,面朝的方向 !! d = 0; if(map[si-1][sj]!='#') d = 1; if(map[si][sj+1]!='#') d = 2; if(map[si+1][sj]!='#') d = 3; } if(map[i][j]=='E') { di = i; dj = j; end.x = i; end.y = j; } } } ldis = rdis = 1; // 把左转优先和右转优先的步数 均初始化为 1(因为'S'(即起点)本身算是第一步!)~ pan = 0; // 把判断变量 赋为 0 leftdfs(si,sj,d); pan = 0; rightdfs(si,sj,d); while (!q.empty()) // 调用 队列 前 先清空(此处必须有,原因在下面的bfs中解释) q.pop(); memset(vis,0,sizeof(vis)); // 标记数组全部初始化为 0 int sd = bfs(); printf("%d %d %d\n",ldis,rdis,sd); } return 0; } int bfs() { start.dis = 1; vis[start.x][start.y] = 1; q.push(start); point hd,next; while (!q.empty()) { hd = q.front(); q.pop(); int k; for(k = 0; k < 4; k++) { next.x = hd.x + X[k]; next.y = hd.y + Y[k]; if(cango(next.x,next.y)) { next.dis = hd.dis + 1; if(next.x == end.x && next.y == end.y) { return next.dis; // 因为此处直接return ,剩下的队列中可能还有元素, //所以每次调用队列前都要先清空 } vis[next.x][next.y] = 1; // 访问过的点标记为 1 q.push(next); } } } } // 此题的dfs中 不能标记数组,因为可能要回退!! void leftdfs(int si,int sj,int d) { int k; for(k=0; k<4; k++) { if(pan ==1) // 此处 也很关键 ,如果没有 会 死循环 !!! return ; int tmp = (d + 3 + k)%4; // 此处 是 每次前进时方向与 k 的关系(请大家动手,很好推~) if(cangoDfs(si+X[tmp],sj+Y[tmp])) { ldis ++; if(si+X[tmp]==di && sj+Y[tmp] == dj) { pan = 1; // 如果找到 终点 就把 pan 的值 变为 1,然后return return ; } leftdfs(si+X[tmp],sj+Y[tmp],tmp); } } return ; } void rightdfs(int si,int sj,int d) { int k; for(k=0; k<4; k++) { if(pan ==1) return ; int tmp = (d + 5 - k)%4; // 此处 是 每次前进时方向与 k 的关系 if(cangoDfs(si+X[tmp],sj+Y[tmp])) { rdis ++; if(si+X[tmp]==di && sj+Y[tmp] == dj) { pan = 1; return ; } rightdfs(si+X[tmp],sj+Y[tmp],tmp); } } return ; } int cango(int i,int j) { if(map[i][j]!='#'&&i>=1&&i<=n&&j>=1&&j<=m&&vis[i][j]==0) return 1; return 0; } int cangoDfs(int i,int j) { if(map[i][j]!='#'&&i>=1&&i<=n&&j>=1&&j<=m) return 1; return 0; }