sicily 1215. 脱离地牢
在一个神秘的国度里,年轻的王子Paris与美丽的公主Helen在一起过着幸福的生活。他们都随身带有一块带磁性的阴阳魔法石,身居地狱的魔王Satan早就想得到这两块石头了,只要把它们熔化,Satan就能吸收其精华大增自己的魔力。于是有一天他趁二人不留意,把他们带到了自己的地牢,分别困在了不同的地方。然后Satan念起了咒语,准备炼狱,界时二人都将葬身于这地牢里。
危险!Paris与Helen都知道了Satan的意图,他们要怎样才能打败魔王,脱离地牢呢?Paris想起了父王临终前留给他的备忘本,原来他早已料到了Satan的野心,他告诉Paris只要把两块魔法石合在一起,念出咒语,它们便会放出无限的光亮,杀死魔王,脱离地牢,而且本子上还附下了地牢的地图,Paris从中了解到了Helen的位置所在。于是他决定首先要找到Helen,但是他发现这个地牢很奇怪,它会增强二人魔法石所带磁力的大小,而且会改变磁力的方向。这就是说,每当Pairs向南走一步,Helen有可能会被石头吸引向北走一步。而这个地狱布满了岩石与熔浆,Pairs必须十分小心,不仅他不能走到岩石或熔浆上,而且由于他行走一步,Helen的位置也会改变,如果Helen碰到岩石上,那么她将停留在原地,但如果Helen移动到了熔浆上,那么她将死去,Paris就找不到她了。
Pairs仔细分析了地图,他找出了一条最快的行走方案,最终与Helen相聚。他们一起念出了咒语"@^&#……%@%&$",轰隆一声,地牢塌陷了,他们又重见光明……
5 5 ##### #H..# #.!.# #.#P# ##### WNSE
5 解释:Paris行走方案为NNWWS,每步过后Helen位置在(2,2), (2,2), (3,2), (4,2), (3,2)。
分析:因为要找的是最短步数,所以用 bfs,
二人在相邻两格移动后碰在了一起的时候,步数算他们移动后的步数。
注意 H 不能移动的情况
当 H 和 P 两者的状态和前面某一状态相同时,说明循环了,也就是要保存两者的状态,注意这里的状态由 H 和 P 两者的位置决定,而不是由其中某一个决定。
#include <iostream> #include <map> #include <queue> #include <cstring> using namespace std; char pict[20][20]; bool visited[20][20][20][20]; struct Node { Node(int Px_ = 0, int Py_ = 0, int Hx_ = 0, int Hy_ = 0, int step_ = 0): Px(Px_), Py(Py_), Hx(Hx_), Hy(Hy_), step(step_) { } int Px, Py, Hx, Hy; int step; }; struct Location { Location(int x_ = 0, int y_ = 0) : x(x_), y(y_) { } int x, y; }; Location P, H; map<char, Location> directionChange; char Hd[4]; char Pd[4] = {'N', 'S', 'W', 'E'}; bool isValid(int x, int y, int n, int m) { return x >= 0 && y >= 0 && x < n && y < m; } int solve(int n, int m) { queue< Node > q; q.push(Node(P.x, P.y, H.x, H.y, 0)); visited[P.x][P.y][H.x][H.y] = true; while (!q.empty()) { Node current = q.front(); q.pop(); if (current.step > 255) { return -1; } current.step++; for (int i = 0; i != 4; ++i) { int Px = current.Px + directionChange[Pd[i]].x; int Py = current.Py + directionChange[Pd[i]].y; int Hx = current.Hx + directionChange[Hd[i]].x; int Hy = current.Hy + directionChange[Hd[i]].y; if (pict[Hx][Hy] == '#') { Hx = current.Hx; Hy = current.Hy; } if (isValid(Px, Py, n, m) && isValid(Hx, Hy, n, m)) { if (pict[Px][Py] == '.' && pict[Hx][Hy] != '!' && !visited[Px][Py][Hx][Hy]) { if ((Hx == current.Px && Hy == current.Py && Px == current.Hx && Py == current.Hy) || (Px == Hx && Py == Hy)) { return current.step; } visited[Px][Py][Hx][Hy] = true; q.push(Node(Px, Py, Hx, Hy, current.step)); } } } } return -1; } int main(int argc, char const *argv[]) { int n, m; while (cin >> n >> m) { memset(visited, false, sizeof(visited)); for (int i = 0; i != n; ++i) { for (int j = 0; j != m; ++j) { cin >> pict[i][j]; if (pict[i][j] == 'P') { pict[i][j] = '.'; P.x = i; P.y = j; } if (pict[i][j] == 'H') { pict[i][j] = '.'; H.x = i; H.y = j; } } } for (int i = 0; i != 4; ++i) { cin >> Hd[i]; } directionChange['N'] = Location(-1, 0); directionChange['S'] = Location(1, 0); directionChange['W'] = Location(0, -1); directionChange['E'] = Location(0, 1); int step = solve(n, m); if (step == -1) cout << "Impossible" << endl; else cout << step << endl; } return 0; }
-----------------------------分割线------------------------------------------
当代码是如下时:
#include <iostream> #include <map> #include <queue> #include <cstring> using namespace std; char pict[20][20]; bool visited[20][20][20][20]; struct Node { Node(int Px_ = 0, int Py_ = 0, int Hx_ = 0, int Hy_ = 0, int step_ = 0): Px(Px_), Py(Py_), Hx(Hx_), Hy(Hy_), step(step_) { } int Px, Py, Hx, Hy; int step; }; struct Location { Location(int x_ = 0, int y_ = 0) : x(x_), y(y_) { } int x, y; }; Location P, H; map<char, Location> directionChange; char Hd[4]; char Pd[4] = {'N', 'S', 'W', 'E'}; bool isValid(int x, int y, int n, int m) { return x >= 0 && y >= 0 && x < n && y < m; } int solve(int n, int m) { queue< Node > q; q.push(Node(P.x, P.y, H.x, H.y, 0)); visited[P.x][P.y][H.x][H.y] = true; while (!q.empty()) { Node current = q.front(); q.pop(); if (current.step > 255) { return -1; } if (current.Px == current.Hx && current.Py == current.Hy) { return current.step; } current.step++; for (int i = 0; i != 4; ++i) { int Px = current.Px + directionChange[Pd[i]].x; int Py = current.Py + directionChange[Pd[i]].y; int Hx = current.Hx + directionChange[Hd[i]].x; int Hy = current.Hy + directionChange[Hd[i]].y; if (pict[Hx][Hy] == '#') { Hx = current.Hx; Hy = current.Hy; } if (isValid(Px, Py, n, m) && isValid(Hx, Hy, n, m)) { if (pict[Px][Py] == '.' && pict[Hx][Hy] != '!' && !visited[Px][Py][Hx][Hy]) { if (Hx == current.Px && Hy == current.Py && Px == current.Hx && Py == current.Hy) { return current.step; } visited[Px][Py][Hx][Hy] = true; q.push(Node(Px, Py, Hx, Hy, current.step)); } } } } return -1; } int main(int argc, char const *argv[]) { int n, m; while (cin >> n >> m) { memset(visited, false, sizeof(visited)); for (int i = 0; i != n; ++i) { for (int j = 0; j != m; ++j) { cin >> pict[i][j]; if (pict[i][j] == 'P') { pict[i][j] = '.'; P.x = i; P.y = j; } if (pict[i][j] == 'H') { pict[i][j] = '.'; H.x = i; H.y = j; } } } for (int i = 0; i != 4; ++i) { cin >> Hd[i]; } directionChange['N'] = Location(-1, 0); directionChange['S'] = Location(1, 0); directionChange['W'] = Location(0, -1); directionChange['E'] = Location(0, 1); int step = solve(n, m); if (step == -1) cout << "Impossible" << endl; else cout << step << endl; } return 0; }
改了 n 次都是 WA,很奇怪,因为这里和第一份代码的效果应该是一样的,因为是广搜,就算是因为在
if (Hx == current.Px && Hy == current.Py && Px == current.Hx && Py == current.Hy) { return current.step; }
这里提前返回了,那也是同一层,那么步数应该是一样的。那么就只有一种情况会出现错误了,如下:
当能达到结果的树形状如上的时候,1 是要在遍历下一层节点的时候才会发现它是不是达到目标状态的,这个时候,2反而先被发现,但 2 并不是最短的,所以此时会出错。