Ignatius and the Princess I HDU - 1026 BFS+优先队列+记录路径

求迷宫最短路径长度和路径。由于有些点会停留打怪,需要多增加停留的时间,而不只是加1。由于队列中每一个扩展点的权重不一样,所以不能按平常的BFS解题。这里采用优先队列,权重大也就是耗时耗路径少的点优先出队。题目还有一个难点就是最短路径。由于BFS就是查找最短路径,所以出列的点构成的路径就是最短路径,我们可以从终点开始往起点搜索,记录扩展点的后继,最后在从起点扩展后继遍历输出到终点。

这里使用功能C++STL的优先队列 priority_queue

优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认按元素值由大到小排序,可以重载“<”操作符来重新定义比较规则。

优先级队列可以用向量(vector)或双向队列(deque)来实现(注意list container不能用来实现queue,因为list的迭代器不是任意存取iterator,而pop中用到堆排序时是要求randomaccess iterator 的!):
priority_queue, less > pq1;     // 使用递增less函数对象排序
priority_queue, greater > pq2;   // 使用递减greater函数对象排序
其成员函数有“判空(empty)” 、“尺寸(Size)” 、“栈顶元素(top)” 、“压栈(push)” 、“弹栈(pop)”等。
Ignatius and the Princess I HDU - 1026 BFS+优先队列+记录路径_第1张图片

#include 
#include 
#include 
#include 
#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;
}

你可能感兴趣的:(ACM,算法)