1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 typedef struct Node
6 {
7 int x, y;
8 }Node;
9
10 const int MAX = 10000;
11 const int N = 110;
12 const int dir[4][2] = { {-1,0}, {0,1}, {1,0}, {0,-1} };//移动方向
13 bool visit[N][N]; //标记数组
14 char board[N][N]; //棋盘
15 int turn[N][N]; //turn数组用于记录棋盘上各点已知的最少的转弯次数
16 Node origin, destination;//起点、终点
17 int maxTurnCount; //路痴所能承受的最大转弯次数
18 int m, n; //棋盘的行、列
19
20
21 inline bool inBoard(const int &row, const int &col)
22 {
23 return 0 <= row && row < m && 0 <= col && col < n;
24 }
25
26 /* dfs搜索(参数含义:行、列、上一次移动的方向) */
27 bool dfs(const int &row, const int &col, int direction)
28 {
29 //在路痴可以承受的转向次数内到达终点
30 if (row == destination.x && col == destination.y && turn[row][col] <= maxTurnCount)
31 return true;
32
33 //转向的次数超过路痴所能承受的次数,路痴已经晕了,因此不可取
34 if (turn[row][col] > maxTurnCount)
35 return false;
36
37 //路痴的位置与终点不在同一行同一列,那么至少需要再转向一次,但是当前的转向次数已经等于路痴所能承受的最大转弯次数
38 if (row != destination.x && col != destination.y && turn[row][col] == maxTurnCount)
39 return false;
40
41 for (int i = 0; i < 4; ++i)
42 {
43 int r = row + dir[i][0];
44 int c = col + dir[i][1];
45 if (true == inBoard(r, c) && '.' == board[r][c] && false == visit[r][c])
46 {
47 /* turn[r][c]记录的是历史上走到(r,c)所用的最短的转弯次数,turn[r][c]的初始值MAX */
48 /* 剪枝1:从(row, col)位置走向(r, c)位置,如果turn[r][c] < turn[row][col],说明之前
49 路痴已经走到过这里了,并且上次的转弯次数比这次小,上次都没走到终点,这次更加走不到。
50 相等的情况不能剪掉,举个栗子:从起点(1, 1),走向终点(3, 5),一种方法是向右走,一种方法
51 是向下走。向右走的时候,走到(3, 4)的时候,转弯次数是1,那么turn[3][4] = 1,在转一次
52 才走到,那么需要转两次;向下走的时候,又走到(3, 4),转弯次数也是1,由于之前走到过,
53 然而两次转弯次数相同,后一种情况(走到终点只需要转弯一次)被剪枝剪掉了,如果题目给
54 出的最大转弯次数是1,那么就得不到正确答案了。
55 ....*
56 .**.*
57 .....
58 */
59 if (turn[r][c] < turn[row][col])
60 continue;
61
62 /* 剪枝2:跟剪枝1的原理类似,从(row, col)走向(r, c),由于方向改变了,因此转弯次数
63 需要加一,转弯次数加一后如果比(r, c)历史上的转弯次数还多,就需要把这种情况给剪掉。
64 相等的情况不能剪掉 */
65 if (-1 != direction && i != direction && turn[r][c] < turn[row][col] + 1)
66 continue;
67
68 if (-1 != direction && i != direction)
69 turn[r][c] = turn[row][col] + 1;//移动方向与之前的不同,转弯次数+1
70 else
71 turn[r][c] = turn[row][col]; //移动方向与之前的相同,转弯次数不变
72
73 visit[r][c] = true;
74
75 if (true == dfs(r, c, i)) return true;
76
77 //回溯
78 visit[r][c] = false;
79 }
80 }
81 return false;
82 }
83
84 void input(void)
85 {
86 scanf("%d %d", &m, &n); //输入行、列
87 for (int i = 0; i < m; ++i) //输入棋盘
88 scanf("%s", board[i]);
89
90 //这里有坑:“5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m)”
91 //1 ≤ x1, x2 ≤ n => x1,x2是列;1 ≤ y1, y2 ≤ m => y1,y2是行
92 //出题人挖的坑,不要被惯性思维束缚了
93 scanf("%d %d %d %d %d", &maxTurnCount, &origin.y, &origin.x, &destination.y, &destination.x);
94
95 //初始化数据
96 for (int i = 0; i < m; ++i)
97 for (int j = 0; j < n; ++j)
98 turn[i][j] = MAX;
99 memset(visit, 0, sizeof(visit));
100 origin.y--; //题目给出的坐标从1开始,因此需要对坐标进行处理,让其从0开始
101 origin.x--;
102 destination.y--;
103 destination.x--;
104 }
105
106 int main(void)
107 {
108 int t;//测试组数
109 scanf("%d", &t);
110 for (int k = 0; k < t; ++k)
111 {
112 input();
113 visit[origin.x][origin.y] = true;
114 turn[origin.x][origin.y] = 0;
115 if (true == dfs(origin.x, origin.y, -1))
116 printf("yes\n");
117 else
118 printf("no\n");
119 }
120 return 0;
121 }