杭电1728————一直WA的BFS(有思维惯性带来的陷阱)
逃离迷宫
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 15281 Accepted Submission(s): 3692
Problem Description
给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,gloria不能走到迷宫外面去。令人头痛的是,gloria是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,gloria所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。gloria能从一个位置走到另外一个位置吗?
Input
第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,
第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能转的弯数,(x1, y1), (x2, y2)表示两个位置,其中x1,x2对应列,y1, y2对应行。
第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能转的弯数,(x1, y1), (x2, y2)表示两个位置,其中x1,x2对应列,y1, y2对应行。
Output
每组测试数据对应为一行,若gloria能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。
Sample Input
2 5 5 ...** *.**. ..... ..... *.... 1 1 1 1 3 5 5 ...** *.**. ..... ..... *.... 2 1 1 1 3
Sample Output
no yes
Source
一开我就很单纯的以为是BFS+一些前驱的小应用而已....写下了如下代码过了很多组测试数据不断WA OTZ
<span style="font-family:Microsoft YaHei;font-size:14px;">#include <cstdio> #include <cstring> #include <queue> #define maxn 105 using namespace std; /*到达的时候转弯数小于maxturn就行*/ struct point{ int x,y; int turn;/*转弯个数*/ int turnx,turny;/*记录向哪转的方向*/ }; int dx[4] = {1,-1,0,0},dy[4] = {0,0,1,-1}; int row,col; int maze[maxn][maxn]; int vis[maxn][maxn]; int maxturn,sx,sy,ex,ey;/*start_x,start_y,end_x,end_y*/ int find; queue<struct point> Q; void read_maze() { char str[maxn]; memset(vis,0,sizeof(vis)); getchar(); for(int i = 1 ; i <= row ; i++) { scanf("%s",str); for(int j = 0 ; j < strlen(str) ; j++) { if(str[j] == '*') maze[i][j+1] = 1; else maze[i][j+1] = 0; } getchar(); } scanf("%d",&maxturn); scanf("%d%d%d%d",&sy,&sx,&ey,&ex); } int inborder(int x,int y) { int flag = 1; if(x < 1 || x > row || y < 1 || y > col) flag = 0; if(maze[x][y] == 1) flag = 0; return flag; } void BFS() { struct point now,next; while(!Q.empty()) Q.pop();/*每次搜索前清空队列*/ now.x = sx; now.y = sy; now.turn = 0; now.turnx = 0; now.turny = 0; vis[sx][sy] = 1; Q.push(now); while(!Q.empty()) { now = Q.front(); Q.pop(); for(int k = 0 ; k < 4 ; k++) { next.x = now.x + dx[k]; next.turnx = dx[k]; next.y = now.y + dy[k]; next.turny = dy[k]; if(inborder(next.x,next.y) && !vis[next.x][next.y]) { vis[next.x][next.y] = 1; if(next.turnx == now.turnx && next.turny == now.turny) /*和之前走的方向不一致也就是说转弯了*/ next.turn = now.turn; else next.turn = now.turn + 1; Q.push(next); if(next.x == ex && next.y == ey && next.turn > maxturn + 1) { find = 0; return ; } if(next.x == ex && next.y == ey && next.turn <= maxturn + 1) { find = 1; return ; } } } } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&row,&col); read_maze();/*经测试正确*/ find = 0;/*默认找不到*/ BFS(); if(find) printf("yes\n"); else printf("no\n"); } return 0; }</span>无限WA是什么情况...想不明白想不明白..(陷入思维定式了肯定想不明白)
只好百度了.....看到了一个AC代码,竟然没有vis数组进行标记,但是作者也没说为什么。又看了一下别人的,大致有了了解。
如图所示:
假设4是目标位置。
假设在2这个坐标转弯数是5,是向下走的,那么4的转弯数是5
假设在3这个坐标转弯数是5,是向下走的,那么4的转弯数就是6
2 . 3 这两个点很明显都是4的上一层(从搜索树来看)但是如果3比2先入队,那么3就先出队列,把4遍历过,4的转弯数就是6了,并且把4标记为已经遍历,那么2后出队就无法遍历4,但是按照假设很显然从2到4的转弯数最少
所以新建一个moveto[i][j]数组表示到达i,j坐标的最小转弯数(初始化为一个很大的数)
<span style="font-family:Microsoft YaHei;font-size:14px;">when moveto[i][j] >= 下一入队结点的转弯数 { moveto[i][j] = 下一结点转弯数; 下一结点入队; } </span>
亲测46MS
<span style="font-family:Microsoft YaHei;font-size:14px;">#include <cstdio> #include <cstring> #include <queue> #define maxn 105 using namespace std; /*到达的时候转弯数小于maxturn就行*/ struct point{ int x,y; int turn;/*转弯个数*/ int dir; }; int dir[4][2] = {{0,1},{-1,0},{1,0},{0,-1}}; int row,col; int maze[maxn][maxn]; int moveto[maxn][maxn];/*记录了移动到(i,j)处需要的转弯数*/ int maxturn,sx,sy,ex,ey;/*start_x,start_y,end_x,end_y*/ int find; queue<struct point> Q; void read_maze() { char str[maxn]; for(int i = 0 ; i <= row ; i++) for(int j = 0 ; j <= col ; j++) moveto[i][j] = 10000; getchar(); for(int i = 1 ; i <= row ; i++) { scanf("%s",str); for(int j = 0 ; j < strlen(str) ; j++) { if(str[j] == '*') maze[i][j+1] = 1; else maze[i][j+1] = 0; } getchar(); } scanf("%d",&maxturn); scanf("%d%d%d%d",&sy,&sx,&ey,&ex); } int inborder(int x,int y) { int flag = 1; if(x < 1 || x > row || y < 1 || y > col) flag = 0; if(maze[x][y] == 1) flag = 0; return flag; } void BFS() { struct point pre,next; while(!Q.empty()) Q.pop();/*每次搜索前清空队列*/ pre.x = sx; pre.y = sy; pre.turn = 0; pre.dir = -1;/*第一次移动不算转弯*/ moveto[sx][sy] = 0;/*初始化转弯数为0*/ Q.push(pre); while(!Q.empty()) { pre = Q.front(); Q.pop(); for(int k = 0 ; k < 4 ; k++) { next = pre; next.x += dir[k][0]; next.y += dir[k][1]; if(!inborder(next.x,next.y)) continue; if(next.dir != k && next.dir != -1)/*==-1是初始点不用算转弯数*/ next.turn ++;/*相当于next.turn = pre.turn + 1;*/ if(next.turn > maxturn) continue; if(moveto[next.x][next.y] >= next.turn) { next.dir = k;/*前边的判断全部是否定的说明是没有转弯的*/ moveto[next.x][next.y] = next.turn; Q.push(next); } if(next.x == ex && next.y == ey) { find = 1; return ; } } } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&row,&col); read_maze();/*经测试正确*/ find = 0;/*默认找不到*/ if(sx == ex && sy == ey) printf("yes\n"); else { BFS(); if(find) printf("yes\n"); else printf("no\n"); } } return 0; }</span>