题目链接:POJ 3083
题意:
给一个迷宫,‘#’代表墙,'.'代表可行,’S‘代表入口,’E‘代表出口。分别求出:
①按左、上、右、下顺序前行从入口到出口的步数。
②按右、上、左、下顺序前行从入口到出口的步数。
③从入口到出口的最短步数。
入口在边上但不在拐角处。入口和出口也各算一步。
分析:
最短路径直接用BFS求解。
先分析第一种要求,即左上右下的顺序,第二种与之类似。
入口在不同的边上下一步走的方向下标变化是不一样的。
①当srow=0时,顺序是: { 0,1 },{ 1,0 },{ 0,-1 },{ -1,0 };【入口在最上面一条边】②当srow=h-1时,顺序是:{ 0,-1 },{ -1,0 },{ 0,1 },{ 1,0 };【入口在最下面一条边】
③当scol=0时,顺序是: { -1,0 },{ 0,1},{ 1,0 },{ 0,-1 };【入口在最左面一条边】
④当scol=w-1时,顺序是: { 1,0 },{ 0,-1 }, { -1,0 },{ 0,1}。【入口在最右边一条边】
仔细观察可以发现:①和②其实是同一个序列【相对顺序是一样的】,③和④是同一个序列。所以可以分两类方向逐个查找,沿右上左下顺序前行也是同样的道理。
在方向上还有一点需要考虑。拿测试一来说,移动的顺序是:
(7,1)->(6,1)->(5,1)->(4,1)->(3,1)->(2,1)->(1,1)->(1,2)->(1,3)->......
注意:(1,1)之前的搜索方向顺序是:{ 0,-1 },{ -1,0 },{ 0,1 },{ 1,0 };转向后,
从(1,2)开始搜索方向顺序变为:{ -1,0 },{ 0,1 },{ 1,0 },{ 0,-1 }。通过多写几组下标变化对比可以发现规律:用dfs传递的方向下标是:(当前方向下标+3)%4
难点:
- 搜索方向的确定
- dfs传递的参数:下一搜索点,下一次开始搜索方向下标,移动步数,搜索方向数组
- 找到出口时返回的是step+1
- 在dfs函数里有两个return:找到出口和return dfs(...)。
参考链接:POJ 3083
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 41;
char s[maxn];
int vis[maxn][maxn],a[maxn][maxn];
int n, w, h, x, y, newx, newy, t;
int srow, scol, erow, ecol, startindex;
//srow,scol,erow,ecol分别是起点终点下标,startindex是开始探索方向下标
int ansbfs, ansleft, ansright;
int flag = 0;
int dirleft1[4][2] = { { 0,1 },{ 1,0 },{ 0,-1 },{ -1,0 } };
int dirleft2[4][2] = { { -1,0 },{ 0,1},{ 1,0 },{ 0,-1 } };
int dirright1[4][2] = { { 0,-1 },{ 1,0 },{ 0,1 },{ -1,0 } };
int dirright2[4][2] = { { 1,0 },{ 0,1 },{ -1,0 },{ 0,-1 } };
int dfs(int x, int y, int k, int step, int dir[4][2])
{
for (int i = 0;i < 4;i++)
{
t = (k + i) % 4;//这样避免了方向下标越界
newx = x + dir[t][0];
newy = y + dir[t][1];
if (newx == erow&&newy == ecol) return step+1;//+1是因为终点也算一个
if(newx < 0 || newy < 0 || newx >= h || newy >= w || a[newx][newy]||(newx==srow&&newy==scol)) continue;
//下标不能越界;遇墙跳过;不能再去起点
return dfs(newx, newy, (t+3)%4, step + 1, dir);
//因为下一次再从左/右边开始时可能已经‘转向’了,例如测试一里从(1,1)到(1,2),所以这里要传递(t+3)%4,而不是k
}
}
int dirbfs[4][2] = { {1,0},{-1,0},{0,1},{0,-1} };
struct Node {
int a;
int b;
int step;
}cur,nextnode;
int bfs()
{
queue<Node> q;
cur.a = srow;
cur.b = scol;
cur.step = 1;
vis[cur.a][cur.b] = 1;
q.push(cur);
while (!q.empty())
{
cur = q.front();
q.pop();
for (int i = 0;i < 4;i++)
{
x = cur.a + dirbfs[i][0];
y = cur.b + dirbfs[i][1];
if (x < 0 || y < 0 || x >= h || y >= w || vis[x][y] || a[x][y]) continue;
nextnode.a = x;
nextnode.b = y;
nextnode.step = cur.step + 1;
vis[x][y] = 1;
if (x == erow&&y == ecol) return cur.step+1;
q.push(nextnode);
}
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
cin >> n;
while (n--)
{
cin >> w >> h;
for (int i = 0;i < h;i++)
{
cin >> s;
for (int j = 0;j < w;j++)
{
if (s[j] == '#') a[i][j] = 1;//a[i][j]=1代表墙
else if (s[j] == '.') a[i][j] = 0;
else if (s[j] == 'S')
{
a[i][j] = 0;
srow = i;
scol = j;
}
else if (s[j] == 'E')
{
a[i][j] = 0;
erow = i;
ecol = j;
}
}
}
ansbfs = ansleft = ansright = 0;
if (srow == 0 || srow == h - 1)
{
if (srow == 0) startindex = 0;
else startindex = 2;
ansleft = dfs(srow, scol, startindex, 1, dirleft1);
ansright = dfs(srow, scol, startindex, 1, dirright1);
}
else if(scol==0||scol==w-1)
{
if (scol == 0) startindex = 0;
else startindex = 2;
ansleft = dfs(srow, scol, startindex, 1, dirleft2);
ansright = dfs(srow, scol, startindex, 1, dirright2);
}
memset(vis, 0, sizeof(vis));
ansbfs = bfs();
cout << ansleft<<" "<<ansright<<" "<<ansbfs << endl;
}
return 0;
}