连连看,不考虑外连,内部连线。好比迷宫搜索路径采用BFS解题。转弯次数,可以采用4个方向每次记录当前方向,如果和上次方向不同那么就是转弯了,也就是记录方向和转弯数。代码实现就是向struct node
中添加方向dir
和转弯数step
。
由于第一步的方向不算转弯,所以step初始值为-1
第一种方法,扩展点重复计算。由于起点到终点有多条路径,每个点的转弯次数会重复计算,在搜索时不能使用vis标记扩展点,这样会定死扩展点的转弯次数,而且不是最优,如果标记当前点,不标记扩展点,那么队列中会出现多个坐标相同的点但是step不同,因为他们属于不同路径的点。
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 10;
char maze[MAXN][MAXN];
int vis[MAXN][MAXN];
int n, m, sx, sy, gx, gy;
struct node
{
int x, y, dir, step;
};
int dx[] = {1, 0, 0, -1};
int dy[] = {0, -1, 1, 0};
bool bfs(int x, int y)
{
//初始化vis
memset(vis, 0, sizeof(vis));
queue<node> q;
node tmp1, tmp2;
tmp1.x = x;
tmp1.y = y;
tmp1.step = -1;//转完次数为-1,第一步方向不算转弯,step=0
tmp1.dir = -1;//初始方向为-1
vis[x][y] = 1;
q.push(tmp1);
while (!q.empty())
{
tmp2 = q.front();
q.pop();
//终点转弯步数<=2满足条件
if (tmp2.x == gx && tmp2.y == gy && tmp2.step <= 2) {return true;}
for (int i = 0; i < 4; ++i)
{
tmp1 = tmp2;
tmp1.x += dx[i];
tmp1.y += dy[i];
tmp1.dir = i;
if (tmp1.x < 0 || tmp1.x >= n || tmp1.y < 0 || tmp1.y >= m || vis[tmp1.x][tmp1.y]) continue;
//下一个点为终点或者下一个点为0
if (maze[tmp1.x][tmp1.y] == '0' || (tmp1.x == gx && tmp1.y == gy))
{
if (tmp1.dir != tmp2.dir)
tmp1.step++;
if (tmp1.step > 2)
continue;
vis[tmp2.x][tmp2.y] = 1;
q.push(tmp1);
}
}
}
return false;
}
int main()
{
int q;
while (cin >> n >> m)
{
if (n == 0 && m == 0) break;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
cin >> maze[i][j];
}
}
cin >> q;
while (q--)
{
cin >> sx >> sy >> gx >> gy;
sx--;
sy--;
gx--;
gy--;
//起、终点相同且不为0
if (maze[sx][sy] != '0' && maze[sx][sy] == maze[gx][gy] && bfs(sx, sy)) cout << "YES\n";
else cout << "NO\n";
}
}
return 0;
}
既然知道了第一种方式的弊端,就是重复计算扩展点
那么每次我们只需要比较当前转弯次数小于等于上一次到达该点的转弯次数即可。
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 10;
char maze[MAXN][MAXN];
int vis[MAXN][MAXN];//记录转弯次数
int n, m, sx, sy, gx, gy;
struct node
{
int x, y, dir, step;
};
int dx[] = {1, 0, 0, -1};
int dy[] = {0, -1, 1, 0};
bool bfs(int x, int y)
{
//初始化vis
memset(vis, 3, sizeof(vis));//初始每个点的转弯次数为3次
queue<node> q;
node tmp1, tmp2;
tmp1.x = x;
tmp1.y = y;
tmp1.step = -1;//转完次数为-1,第一步方向不算转弯,step=0
tmp1.dir = -1;//初始方向为-1
vis[x][y] = 1;
q.push(tmp1);
while (!q.empty())
{
tmp2 = q.front();
q.pop();
//终点转弯步数<=2满足条件
if (tmp2.x == gx && tmp2.y == gy && tmp2.step <= 2) return true;
//else if (tmp2.step > 2) return false;
for (int i = 0; i < 4; ++i)
{
tmp1.step = tmp2.step;
tmp1.x = tmp2.x + dx[i];
tmp1.y = tmp2.y + dy[i];
tmp1.dir = i;
if (tmp1.x < 0 || tmp1.x >= n || tmp1.y < 0 || tmp1.y >= m ) continue;
//下一个点为终点或者下一个点为0
if (maze[tmp1.x][tmp1.y] == '0' || tmp1.x == gx && tmp1.y == gy)
{
if (tmp1.dir != tmp2.dir)
tmp1.step++;
if (tmp1.step > 2)
continue;
if (tmp1.step > vis[tmp1.x][tmp1.y])//当前转弯次数小于等于上次转弯次数,添加该扩展点
continue;
vis[tmp1.x][tmp1.y] = tmp1.step;
q.push(tmp1);
}
}
}
return false;
}
int main()
{
int q;
while (cin >> n >> m)
{
if (n == 0 && m == 0) break;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
cin >> maze[i][j];
}
}
cin >> q;
while (q--)
{
cin >> sx >> sy >> gx >> gy;
sx--;
sy--;
gx--;
gy--;
//起、终点相同且不为0
if (maze[sx][sy] != '0' && maze[sx][sy] == maze[gx][gy] && bfs(sx, sy)) cout << "YES\n";
else cout << "NO\n";
}
}
return 0;
}
没有过多的加入扩展点,总是保证当前路径最优。两种方法很明显,第二种更快。