杭电3085 Nightmare Ⅱ(双向bfs)(曼哈顿距离)

博客参考http://blog.csdn.net/u011932355/article/details/44344725

Nightmare Ⅱ

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1347    Accepted Submission(s): 314


Problem Description
Last night, little erriyue had a horrible nightmare. He dreamed that he and his girl friend were trapped in a big maze separately. More terribly, there are two ghosts in the maze. They will kill the people. Now little erriyue wants to know if he could find his girl friend before the ghosts find them.
You may suppose that little erriyue and his girl friend can move in 4 directions. In each second, little erriyue can move 3 steps and his girl friend can move 1 step. The ghosts are evil, every second they will divide into several parts to occupy the grids within 2 steps to them until they occupy the whole maze. You can suppose that at every second the ghosts divide firstly then the little erriyue and his girl friend start to move, and if little erriyue or his girl friend arrive at a grid with a ghost, they will die.
Note: the new ghosts also can devide as the original ghost.
 

Input
The input starts with an integer T, means the number of test cases.
Each test case starts with a line contains two integers n and m, means the size of the maze. (1<n, m<800)
The next n lines describe the maze. Each line contains m characters. The characters may be:
‘.’ denotes an empty place, all can walk on.
‘X’ denotes a wall, only people can’t walk on.
‘M’ denotes little erriyue
‘G’ denotes the girl friend.
‘Z’ denotes the ghosts.
It is guaranteed that will contain exactly one letter M, one letter G and two letters Z. 
 

Output
Output a single integer S in one line, denotes erriyue and his girlfriend will meet in the minimum time S if they can meet successfully, or output -1 denotes they failed to meet.
 

Sample Input
   
   
   
   
3 5 6 XXXXXX XZ..ZX XXXXXX M.G... ...... 5 6 XXXXXX XZZ..X XXXXXX M..... ..G... 10 10 .......... ..X....... ..M.X...X. X......... .X..X.X.X. .........X ..XX....X. X....G...X ...ZX.X... ...Z..X..X
 

Sample Output
   
   
   
   
1 1 -1

题目大意:

一张n*m大的地图上有五种东西分别:

‘. ’:路

‘X':墙

’Z':鬼魂:每一个单位时间开始的时候会衍生出新的鬼 每个单位时间开始的时候都衍生出两个单位长度.(一圈)

‘M’:主人公.腿脚比较麻溜 每一个单位时间内可以行动3次(移动三个格子).

‘G’:主人公的女朋友 每一个单位时间内可以行动一次(移动1个格子).

问两人是否可以成功碰面,再不被鬼吃掉的前提下

思路:
双向广搜 .应用曼哈顿距离判断是否在鬼魂的范围内.

这里先贴上初始化的变量部分:

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

const int dir[4][2]={ {1,0},{-1,0},{0,1},{0,-1} };//走法
char str[810][810];//地图
int used[2][810][810];//标记是否走过
int g_x,g_y,m_x,m_y,n,m,step;//主人公女朋友的坐标, 主人公的坐标,step是回合(多少单位时间).<span style="font-family: Arial;"> </span>
并且初始化init

void init()
{
    int i,j,cnt;
    scanf("%d%d",&n,&m);
    for (i=0;i<n;i++)
        scanf("%s",str[i]);//读图
    cnt=0;
    for (i=0;i<n;i++)
        for (j=0;j<m;j++)//初始化各种标记
        {
            if (str[i][j]=='G')
            {
                g_x=i; g_y=j;
            }
            if (str[i][j]=='M')
            {
                m_x=i; m_y=j;
            }
            if (str[i][j]=='Z')
            {
                z[cnt].x=i;
                z[cnt].y=j;
                cnt++;
            }
        }
}

然后我们进入算法的主体:
双向bfs.不需要一个三维的图 但是需要一个三维的vis数组.

用于标记主人公和他女朋友走过的位子.

简单bfs可知用一个队列就能完成 这里当然要用两个队列来完成.一个队列用于存主人公走到的位子 一个队列用于存女朋友走到的位子..

既然是双向bfs有两个物体在行动 即使是同时行动,我们用代码实现也是很难的 所以我们先看成一个先动 另一个后动.这样就能完成双向bfs的实现

至于如何实现走3步 这里读者可以先想一想 并对应下列代码思考:

    sum=q[w].size();
    while (sum--)

是不是稍微有了一点点思路呢?(当然一定有其他不同于我的方法可以解这个题.)

这里贴上代码和详解:

int bfs(int w)//w如果是0表示是主人公 如果是1表示是女朋友.//这里我要准备走多次.//下边一组代码贴上如何实现走多次
{
    node cur,next;
    int i,sum;
    sum=q[w].size();//因为我要准备走多次 而不是一次一个人 走完全部能走的路.请体会一下.
    while (sum--)
    {
        cur=q[w].front();
        q[w].pop();
        if (judge(cur)==0) continue;//上一轮这个点没有被鬼魂覆盖 不代表这一轮就没有被鬼魂覆盖~
        for (i=0;i<4;i++)
        {
            next.x=cur.x+dir[i][0];
            next.y=cur.y+dir[i][1];
            if (judge(next)==0) continue;
            if (used[w][next.x][next.y]==0)
            {
                if (used[1-w][next.x][next.y]==1) return 1;//如果女朋友能走到的地方 主人公能走的到(在合法的情况下(各种判断一定合法了)就代表两个人能相遇)
                used[w][next.x][next.y]=1;
                q[w].push(next);
            }
        }
    }
    return 0;
}



    while ((!q[0].empty()) || (!q[1].empty()))//如果有一方没有能走的方式了 就代表game over了
    {
        step++;//回合++
        if (bfs(0)==1) return step;
        if (bfs(0)==1) return step;
        if (bfs(0)==1) return step;//男主走3次
        if (bfs(1)==1) return step;//女朋友走1次.
    }//如此一来就能实现男主走三次之后女朋友走一次了(两者不能同时动 就先定一个 走另一个 (而且想用队列控制男主走3次不太现实 这样实现还是比较容易理解的).)
所以就有了以上的int sum=s[w].size();的操作了,每一次男主行走都把上一步能走到的点都继续向下走了 而且没有涉及到这次走完的步向下走

//这里只可意会 不可言传 也许我这里语言描叙并不是恰到好处 且行且珍惜.

那么如何判断现在我走到的地方没有被鬼魂所覆盖呢?

这里涉及到一个曼哈顿距离的问题~


这里贴上一个曼哈顿距离的算法讲解帖子(百度搜的)http://www.cnblogs.com/jiahuafu/p/4013873.html



int judge(node b)
{
    if (b.x<0 || b.y<0 || b.x>=n || b.y>=m) return 0;
    if (str[b.x][b.y]=='X') return 0;
    if ((abs(b.x-z[0].x)+abs(b.y-z[0].y))<=2*step) return 0;//曼哈顿距离判断问题.
    if ((abs(b.x-z[1].x)+abs(b.y-z[1].y))<=2*step) return 0;
    return 1;
}

蓝后这里贴上完整的AC代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;

const int dir[4][2]={ {1,0},{-1,0},{0,1},{0,-1} };
char str[810][810];
int used[2][810][810];
int g_x,g_y,m_x,m_y,n,m,step;

struct node
{
    int x,y;
}ncur,z[2];

queue<node>q[2];

void init()
{
    int i,j,cnt;
    scanf("%d%d",&n,&m);
    for (i=0;i<n;i++)
        scanf("%s",str[i]);
    cnt=0;
    for (i=0;i<n;i++)
        for (j=0;j<m;j++)
        {
            if (str[i][j]=='G')
            {
                g_x=i; g_y=j;
            }
            if (str[i][j]=='M')
            {
                m_x=i; m_y=j;
            }
            if (str[i][j]=='Z')
            {
                z[cnt].x=i;
                z[cnt].y=j;
                cnt++;
            }
        }
}

int judge(node b)
{
    if (b.x<0 || b.y<0 || b.x>=n || b.y>=m) return 0;
    if (str[b.x][b.y]=='X') return 0;
    if ((abs(b.x-z[0].x)+abs(b.y-z[0].y))<=2*step) return 0;//曼哈顿距离判断问题.
    if ((abs(b.x-z[1].x)+abs(b.y-z[1].y))<=2*step) return 0;
    return 1;
}

int bfs(int w)
{
    node cur,next;
    int i,sum;

    sum=q[w].size();
    while (sum--)
    {
        cur=q[w].front();
        q[w].pop();
        if (judge(cur)==0) continue;
        for (i=0;i<4;i++)
        {
            next.x=cur.x+dir[i][0];
            next.y=cur.y+dir[i][1];
            if (judge(next)==0) continue;
            if (used[w][next.x][next.y]==0)
            {
                if (used[1-w][next.x][next.y]==1) return 1;
                used[w][next.x][next.y]=1;
                q[w].push(next);
            }
        }
    }
    return 0;
}
int solve()
{
    while (!q[0].empty()) q[0].pop();
    while (!q[1].empty()) q[1].pop();

    ncur.x=m_x;
    ncur.y=m_y;
    q[0].push(ncur);
    ncur.x=g_x;
    ncur.y=g_y;
    q[1].push(ncur);
    memset(used,0,sizeof(used));
    used[0][m_x][m_y]=used[1][g_x][g_y]=1;
    step=0;

    while ((!q[0].empty()) || (!q[1].empty()))
    {
        step++;
        if (bfs(0)==1) return step;
        if (bfs(0)==1) return step;
        if (bfs(0)==1) return step;
        if (bfs(1)==1) return step;
    }
    return -1;
}
int main()
{
    int t;
    scanf("%d",&t);
    while (t--)
    {
        init();
        printf("%d\n",solve());
    }
    return 0;
}









你可能感兴趣的:(杭电,bfs,双向BFS,杭电3085,杭电OJ3085)