連連看,不考慮外連,內部連線。好比迷宮搜索路徑採用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;
}
沒有過多的加入擴展點,總是保證當前路徑最優。兩種方法很明顯,第二種更快。