求迷宮最短路徑長度和路徑。由於有些點會停留打怪,需要多增加停留的時間,而不只是加1。由於隊列中每一個擴展點的權重不一樣,所以不能按平常的BFS解題。這裏採用優先隊列,權重大也就是耗時耗路徑少的點優先出隊。題目還有一個難點就是最短路徑。由於BFS就是查找最短路徑,所以出列的點構成的路徑就是最短路徑,我們可以從終點開始往起點搜索,記錄擴展點的後繼,最後在從起點擴展後繼遍歷輸出到終點。
這裏使用功能C++STL的優先隊列 priority_queue
優先隊列容器與隊列一樣,只能從隊尾插入元素,從隊首刪除元素。但是它有一個特性,就是隊列中最大的元素總是位於隊首,所以出隊時,並非按照先進先出的原則進行,而是將當前隊列中最大的元素出隊。這點類似於給隊列裏的元素進行了由大到小的順序排序。元素的比較規則默認按元素值由大到小排序,可以重載“<”操作符來重新定義比較規則。
優先級隊列可以用向量(vector)或雙向隊列(deque)來實現(注意list container不能用來實現queue,因爲list的迭代器不是任意存取iterator,而pop中用到堆排序時是要求randomaccess iterator 的!):
priority_queue<vector<int>, less<int> > pq1; // 使用遞增less<int>函數對象排序
priority_queue<deque<int>, greater<int> > pq2; // 使用遞減greater<int>函數對象排序
其成員函數有“判空(empty)” 、“尺寸(Size)” 、“棧頂元素(top)” 、“壓棧(push)” 、“彈棧(pop)”等。
#include <queue>
#include <iostream>
#include <cstring>
#include <cstdio>
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std;
const int MAXN = 105;
char maze[MAXN][MAXN];//迷宮
int vis[MAXN][MAXN];//判斷訪問數組,1爲已經訪問,0爲未訪問
int n, m;//行、列
int dx[] = {1, 0, -1, 0};//方向矢量
int dy[] = {0, -1, 0, 1};
struct Point
{
int x, y, time;//座標和花費的時間
bool operator < (const Point& a) const
{
return time > a.time;
}
};
struct nextnode
{
int x, y;
}Next[MAXN][MAXN];//後繼數組
bool bfs(int x, int y)
{
vis[x][y] = 1;
Next[x][y].x = -1;//終點無後繼
priority_queue<Point> q;
Point cur, tmp;
cur.x = x; cur.y = y; cur.time = 0;
if (isdigit(maze[x][y])) cur.time += maze[x][y] - '0';//終點可以有野怪,如果是數字字符,打怪停留
q.push(cur);
while (!q.empty())
{
cur = q.top();
q.pop();
if (cur.x == 0 && cur.y == 0)//到達起點
{
int sx = cur.x, sy = cur.y;
int t = 1;
printf("It takes %d seconds to reach the target position, let me show you the way.\n", cur.time);
while (Next[sx][sy].x != -1)
{
int nx = Next[sx][sy].x, ny = Next[sx][sy].y;
printf("%ds:(%d,%d)->(%d,%d)\n", t++, sx, sy, nx, ny);
if (isdigit(maze[nx][ny]))
_for (i, 0, maze[nx][ny] - '0')
printf("%ds:FIGHT AT (%d,%d)\n", t++, nx, ny);
sx = nx;
sy = ny;
}
return true;
}
_for (i, 0, 4)
{
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || vis[nx][ny] || maze[nx][ny] == 'X') continue;
vis[nx][ny] = 1;
tmp.x = nx; tmp.y = ny; tmp.time = cur.time + 1;
if (isdigit(maze[nx][ny])) tmp.time += maze[nx][ny] - '0';
//tmp的後繼爲cur點,不能設置前驅數組,因爲cur的前驅有多個(多個擴展點在隊列中)
Next[nx][ny].x = cur.x;
Next[nx][ny].y = cur.y;
q.push(tmp);
}
}
return false;
}
int main()
{
while (cin >> n >> m)
{
memset(vis, 0, sizeof(vis));
_for (i, 0, n) _for (j, 0, m) cin >> maze[i][j];
if (!bfs(n - 1, m - 1)) cout << "God please help our poor hero.\n";
cout << "FINISH\n";
}
return 0;
}