3305: Hero In Maze II (优先队列+bfs)

99封情书 提交于 2019-12-21 21:17:41

Description

 

500年前,Jesse是我国最卓越的剑客。他英俊潇洒,而且机智过人^_^。
突然有一天,Jesse心爱的公主被魔王困在了一个巨大的迷宫中。Jesse听说这个消息已经是两天以后了,他急忙赶到迷宫,开始到处寻找公主的下落。令人头痛的是,Jesse是个没什么方向感的人,因此,他在行走过程中,不能转太多弯了,否则他会晕倒的。 我们假定Jesse和公主所在的位置都是空地,初始时,Jesse所面向的方向未定,他可以选择4个方向的任何一个出发,而不算成一次转弯。希望你帮他判断一下他是否有机会找到心爱的公主。 

 

Input

 

题目包括多组测试数据.

第1行为一个整数T(1 ≤ T≤ 100),表示测试数据的个数,接下来为T组测试数据.

每组测试数据以两个整数N,M,K(1<=N, M≤100, 0<K<=10)开头,分别代表迷宫的高,长和Jesse最多能转的弯数,(紧接着有N行,M列字符,由".","*","P","S"组成。其中
"." 代表能够行走的空地。
"*" 代表墙壁,Jesse不能从此通过。
"P" 是公主所在的位置。
"S" 是Jesse的起始位置。
每个时间段里Jesse只能选择“上、下、左、右”任意一方向走一步。

 

Output

 

如果Jesse能在晕之前找到公主,输出“YES”,否则输出“NO”。

 

Sample Input

2
5 5 1
P..**
*.**.
S....
.....
*....
5 5 2
P..**
*.**.
S....
.....
*....

Sample Output

 NO

YES

http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=3305

 

 

很久之前就过了这题了,当时是用dfs过的,我也觉得会超时的了,但是提交上去10ms过了,看来还是数据太弱了。必须会超时的,网上其他代码的dfs都会超时,我试过了

40 40 10
P.*.....................................
**......................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
........................................
.......................................S

这样他们的代码就超时了,关于dfs怎么做,应该很容易想的,用dfs(x,y,face,turn)表示在[x,y]那里,面向face那里,转弯次数是turn次的情况,直接dfs即可,唯一的剪枝也就是if (turn>k),就是已经晕了就不行了吧?对于我上面那组数据都超时了,就不用想100*100的矩阵了。

dfs  AC代码(不是正解)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (1<<28)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
int flag;
int n,m,k;
const int maxn=1e2+20;
char str[maxn][maxn];
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
bool book[maxn][maxn];
void dfs (int x,int y,int face,int turn)
{
    if (turn>k) return ;//已经晕了就不行了
    if (str[x][y]=='P')
    {
        flag=1;//找到公主了
        return ;
    }
    for (int i=0;i<4;i++)
    {
        int tx=x+next[i][0];
        int ty=y+next[i][1];
        if (!flag&&tx>=1&&tx<=n&&ty>=1&&ty<=m&&str[tx][ty]!='*'&&book[tx][ty]==0)
        {
            book[tx][ty]=1;
            if (face!=i)
            {
                dfs(tx,ty,i,turn+1);
            }
            else dfs(tx,ty,i,turn);
            book[tx][ty]=0;
        }
    }
    return ;
}
void work ()
{
    scanf ("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++)
    {
        scanf ("%s",str[i]+1);
    }
    int bx,by;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            if (str[i][j]=='S')
            {
                bx=i;
                by=j;
            }
        }
    }
    flag=0;
    for (int i=0;i<4;i++)
    {
        int tx=bx+next[i][0];
        int ty=by+next[i][1];
        if (tx>=1&&tx<=n&&ty>=1&&ty<=m&&str[tx][ty]!='*')
        {
            memset (book,0,sizeof(book));
            if (str[tx][ty]=='P')
            {
                printf ("YES\n");
                return ;
            }
            dfs(tx,ty,i,0);
            if (flag)
            {
                printf ("YES\n");
                return ;
            }
        }
    }
    printf ("NO\n");
    return ;
}
int main ()
{
    #ifdef local
    freopen("data.txt","r",stdin);
    #endif
    int t;
    scanf ("%d",&t);
    while (t--)
    {
        work ();
    }
    return 0;
}
View Code

这题的正解应该是bfs,我开始的时候,写朴素的bfs无限WA,开始的时候想不出数据,就上网搜,用dfs过了。直到现在,才发现是要用优先队列优化一个bfs,每次pop出当前转弯次数最小的点来转移枚举,这样才能确保答案的最优性。关于怎么证明,我用一组样例来说明,是和本题无关的题目。

题意:大概是这样的,人物P要去救人物S,途中有警察X,没经过一个警察,就要杀死他,耗时+1,其他的,直接一步到达。现在问P-->S的最短时间。

直接用朴素的bfs是会wa的了。这个也应该用优先队列优化。因为bfs是有次序之分的。

.....

p....

.x...

.x...

.S...

明显这组样例输出的是4,但是bfs输出的是6,为什么呢?

首先得看你bfs的反向是怎样的,我的是右--下--左--上。

那么,第一个入队的点,是p右边那个,然后每次扩展的时候,第一个扩展的就是它了,走向了x(3,2),这是没问题的,因为现在走向x的点,步数都是最小的。但是问题来了。走向x(4,2)这个点的会是谁呢?答案是x(3,2),为什么呢?因为它先入队,所以先搜它。照这样下去,走向S的点,就会是x(4,2)了。所以最优性错误。

下面的bfs正解代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (1<<28)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>

int n,m,k;
const int maxn=1e2+20;
char str[maxn][maxn];
int next[4][2]= {{0,1},{1,0},{0,-1},{-1,0}};
struct coor
{
    int x,y,count;
    int face;
    friend bool operator < (const coor a,const coor b)
    {
        return a.count>b.count;
    }
};
int lbx,lby;
int bfs (int bx,int by,int face)
{
    bool book[maxn][maxn]= {0};
    book[lbx][lby]=1;
    book[bx][by]=1;
    priority_queue<struct coor>que;
    struct coor t;
    struct coor b;
    t.x=bx;
    t.y=by;
    t.count=0;
    t.face=face;
    que.push(t);
    while (!que.empty())
    {
        t=que.top();
        que.pop();
        if (t.count>k) continue;
        for (int i=0; i<4; i++)
        {
            b.x=t.x+next[i][0];
            b.y=t.y+next[i][1];
            if (b.x>=1&&b.x<=n&&b.y>=1&&b.y<=m&&str[b.x][b.y]!='*'&&book[b.x][b.y]==0)
            {
                book[b.x][b.y]=1;
                if (t.face!=i)
                {
                    b.count=t.count+1;
                }
                else b.count=t.count;
                b.face=i;
                if (str[b.x][b.y]=='P')
                {
                    return b.count;
                }
                que.push(b);
            }
        }
    }
    return inf;
}
void work ()
{
    scanf ("%d%d%d",&n,&m,&k);
    for (int i=1; i<=n; i++)
    {
        scanf ("%s",str[i]+1);
    }
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=m; j++)
        {
            if (str[i][j]=='S')
            {
                lbx=i;
                lby=j;
            }
        }
    }
    for (int i=0; i<4; i++)
    {
        int tx=lbx+next[i][0];
        int ty=lby+next[i][1];
        if (tx>=1&&tx<=n&&ty>=1&&ty<=m&&str[tx][ty]!='*')
        {
            if (str[tx][ty]=='P')
            {
                printf ("YES\n");
                return ;
            }
            int ans=bfs(tx,ty,i);
            if (ans<=k)
            {
                //printf ("%d\n",i);
                printf ("YES\n");
                return ;
            }
            //printf ("%d  %d\n",ans,i);
        }
    }
    printf ("NO\n");
    return ;
}

int main ()
{
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    int t;
    scanf ("%d",&t);
    while (t--)
    {
        work ();
    }
    return 0;
}
View Code

还有我看到有人说这题只能用dfs不能用bfs,那是错误的。希望其他人别用dfs了。用bfs吧。这才是正解。

本人初出茅庐,如果有哪里错误的地方,还请读者多多指出,本人感激不尽!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!