hdu 1026 Ignatius and the Princess I【优先队列+BFS】

链接:



Ignatius and the Princess I

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10006    Accepted Submission(s): 3004
Special Judge


Problem Description
The Princess has been abducted by the BEelzebub feng5166, our hero Ignatius has to rescue our pretty Princess. Now he gets into feng5166's castle. The castle is a large labyrinth. To make the problem simply, we assume the labyrinth is a N*M two-dimensional array which left-top corner is (0,0) and right-bottom corner is (N-1,M-1). Ignatius enters at (0,0), and the door to feng5166's room is at (N-1,M-1), that is our target. There are some monsters in the castle, if Ignatius meet them, he has to kill them. Here is some rules:

1.Ignatius can only move in four directions(up, down, left, right), one step per second. A step is defined as follow: if current position is (x,y), after a step, Ignatius can only stand on (x-1,y), (x+1,y), (x,y-1) or (x,y+1).
2.The array is marked with some characters and numbers. We define them like this:
. : The place where Ignatius can walk on.
X : The place is a trap, Ignatius should not walk on it.
n : Here is a monster with n HP(1<=n<=9), if Ignatius walk on it, it takes him n seconds to kill the monster.

Your task is to give out the path which costs minimum seconds for Ignatius to reach target position. You may assume that the start position and the target position will never be a trap, and there will never be a monster at the start position.
 

Input
The input contains several test cases. Each test case starts with a line contains two numbers N and M(2<=N<=100,2<=M<=100) which indicate the size of the labyrinth. Then a N*M two-dimensional array follows, which describe the whole labyrinth. The input is terminated by the end of file. More details in the Sample Input.
 

Output
For each test case, you should output "God please help our poor hero." if Ignatius can't reach the target position, or you should output "It takes n seconds to reach the target position, let me show you the way."(n is the minimum seconds), and tell our hero the whole path. Output a line contains "FINISH" after each test case. If there are more than one path, any one is OK in this problem. More details in the Sample Output.
 

Sample Input
5 6 .XX.1. ..X.2. 2...X. ...XX. XXXXX. 5 6 .XX.1. ..X.2. 2...X. ...XX. XXXXX1 5 6 .XX... ..XX1. 2...X. ...XX. XXXXX.
 

Sample Output
It takes 13 seconds to reach the target position, let me show you the way. 1s:(0,0)->(1,0) 2s:(1,0)->(1,1) 3s:(1,1)->(2,1) 4s:(2,1)->(2,2) 5s:(2,2)->(2,3) 6s:(2,3)->(1,3) 7s:(1,3)->(1,4) 8s:FIGHT AT (1,4) 9s:FIGHT AT (1,4) 10s:(1,4)->(1,5) 11s:(1,5)->(2,5) 12s:(2,5)->(3,5) 13s:(3,5)->(4,5) FINISH It takes 14 seconds to reach the target position, let me show you the way. 1s:(0,0)->(1,0) 2s:(1,0)->(1,1) 3s:(1,1)->(2,1) 4s:(2,1)->(2,2) 5s:(2,2)->(2,3) 6s:(2,3)->(1,3) 7s:(1,3)->(1,4) 8s:FIGHT AT (1,4) 9s:FIGHT AT (1,4) 10s:(1,4)->(1,5) 11s:(1,5)->(2,5) 12s:(2,5)->(3,5) 13s:(3,5)->(4,5) 14s:FIGHT AT (4,5) FINISH God please help our poor hero. FINISH
 

Author
Ignatius.L
 


题意:


给你一个 N*M 的图, 要你从第一个点走到最后一个点【从左上角走到右下角】
只可以按照上下左右四个方向走


. : 代表可以走
 
X :  表示是墙,不可以走


n :  代表这里有一个怪兽,打败怪兽用时 n


每走一步耗时 1 .


如果能到达,则输出最小时间和每一步的走法
不能到达输出。。。
具体输出看样例。


注意:保证起点没有怪兽,终点不是墙。【也就是说终点可能有怪兽】




算法:优先队列+BFS 【本质Dijkstra】


思路:


很裸的最短路了,但是不熟悉优先队列,用自己的笨方法错了很久。


正解思路

由于是用的优先队列+BFS 为了方便输出路径,所以从终点开始搜索
从起点开始也可以,只是不方便,要么递归输出路径点击打开链接
要么定义一个数组来维护,使得本来就很复杂了的简单题目越发变得复杂起来。

定义一个优先队列,包含了位置和当前位置到达终点的最短时间。
优先队列保证时间短的先出队。。。
//定义优先队列:对于入队了的点,先出队的是时间少的,那么第一个到达终点的就是结果
struct Node{
    int x,y; //当前到达的点
    int time; //耗费的时间

    bool operator < (const Node &b) const{
        return b.time < time;
    }
};

关于优先队列的重载一直不是很清楚。。。
下面我是这么理解的,每次新加入一个点 b ,不管他们的 x 和 y 也就是默认关于 x 和 y 按照普通的队列定义,先进先出

然后优先考虑 time ,b.time 与原来的队列中的每一个 time 比较【从队首往队伍遍历】如果一旦遇到了 b.time < time
那么就把 " b 插入到当前遍历到的位置". 有待路过的大神详细指教下。
 
由于优先队列保证了按照时间少的先出队,那么第一个遍历到起点的一定是所求的最短路了。
后面可能会有其它的路,但是不会比这条路更优。【special judge】

下面我们考虑输出问题:定义一个先驱结构体记录每一个点的前一个点的位置就好了。
注意:由于搜索的时候为了方便输出路径是从终点往起点搜索的,那么先驱中记录的点其实是实际走的过程中的下一个点。

此题参考博客:点击打开链接


 
我的开始错误思路分析

开始没有想到用优先队列搜索,由于前面做过 BFS 倒水问题POJ 3414 Pots【bfs模拟倒水问题】 也是要模拟输出路径
就感觉很像,其实错了。。。然后按照这题的思路写。
也考虑了会有多条路到达,但是想到点很少才 100*100 就准备直接暴力搜索完所有的点。
开始想的是,一旦到达终点就和原来的比较看能否更优【我以为那个方法会多次到达终点】
其实我入队的时候标记了,出队却没有标记,所以最多只可能到达一次终点,根本不可能多次到达终点,更新最小时间。
但是样例出来了,自己怎么想也想不出错误我的错误思路代码

后来请教了下南城边,他给我指出了上面提到的问题,更本不可能更新最小时间。因为按照上面的写法,终点只可能入队和出队一次。
然后我又准备用最短路的思想模拟,但是还是准备在上面的基础上改一下,于是每一个点出队我也标记了下,这样保证了终点可以多次遍历到
但是这样一来就无法解决跳出 BFS 问题,于是我想到每一个点最多被上下左右的点走一次,那么是不是标记每一个点最多入队四次就可以了呢?
样例同样出来了,结果还是 WA 。。。
想了下:可能是我虽然标记了入队四次,但是可能这四次都是由同一个点到达的,那么也和标记一次没有分别了,还是无法解决上面的更新最短时间问题。

所以:最终还是只能按照最短路的思想遍历。

很裸的题目了,本来只是打算练下手,结果还是做了很久。菜鸟要努力 Fighting !!!Come on

code:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 110;
const int INF = maxn*maxn*10;

int map[maxn][maxn]; //记录图
int vis[maxn][maxn]; //标记入队
char str[maxn];
int n,m;

int dir[4][2] = {0,1, 0,-1, -1,0, 1,0};

//定义优先队列:对于入队了的点,先出队的是时间少的,那么第一个到达终点的就是结果
struct Node{
    int x,y; //当前到达的点
    int time; //耗费的时间

    bool operator < (const Node &b) const{
        return b.time < time;
    }
};

//每一个点的前驱, 由于是逆向搜索的, 所以记录的其实是当前点的下一个点了
struct Pre{
    int px, py;
}pre[maxn][maxn];

void bfs()
{
    Node now, next;
    priority_queue<Node> q;
    while(!q.empty()) q.pop();

    now.x = n; now.y = m; //从终点走向起点
    now.time = map[n][m]; //注意:终点也可能会有怪兽
    pre[n][m].px = -1; //输出边界
    q.push(now);

    memset(vis, 0, sizeof(vis)); //为方便快速输出路径, 从终点往起点找
    vis[n][m] = 1; //标记终点入队

    while(!q.empty())
    {
        now = q.top(); q.pop();

        if(now.x == 1 && now.y == 1) //一旦到达起点
        {
            printf("It takes %d seconds to reach the target position, let me show you the way.
", now.time);
            int time = 1;
            int x = now.x, y = now.y; //当前的位置
            int nx = pre[x][y].px, ny = pre[x][y].py; //下一个位置
            while(pre[x][y].px != -1) //不停的找前驱
            {
                printf("%ds:(%d,%d)->(%d,%d)
", time++, x-1, y-1, nx-1, ny-1);
                while(map[nx][ny]--) //如果有怪兽
                {
                    printf("%ds:FIGHT AT (%d,%d)
", time++, nx-1, ny-1);
                }
                x = nx; y = ny; //继续查找下一个点
                nx = pre[x][y].px, ny = pre[x][y].py;
            }
            printf("FINISH
");
            return; //结束
        }

        for(int i = 0; i < 4; i++)
        {
            next.x = now.x+dir[i][0];
            next.y = now.y+dir[i][1];

            if(map[next.x][next.y] >= 0 && !vis[next.x][next.y]) //当前点可以走,并且没有入队过
            {
                vis[next.x][next.y] = 1; //标记入队

                next.time = now.time + 1 + map[next.x][next.y];
                pre[next.x][next.y].px = now.x; //前驱记录
                pre[next.x][next.y].py = now.y;

                q.push(next);
            }
        }
    }

    printf("God please help our poor hero.
"); //不能到达
    printf("FINISH
");
    return;
}

int main()
{
    while(scanf("%d%d", &n,&m) != EOF)
    {
        gets(str);
        for(int i = 0; i <= n+1; i++) //周围加边
            for(int j = 0; j <= m+1; j++)
                map[i][j] = -1;

        char c;
        for(int i = 1; i <= n; i++) //输出的时候注意 -1 处理下
        {
            for(int j = 1; j <= m; j++)
            {
                scanf("%c", &c);
                if(c != 'X')
                {
                    if(c == '.') map[i][j] = 0;
                    else map[i][j] = c-'0';
                }
            }
            gets(str);
        }

        bfs();
    }
    return 0;
}


 
原文地址:https://www.cnblogs.com/riskyer/p/3263229.html