【XSY2498】贪吃蛇(bfs_dfs)

题面

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input & Sample Output

【样例输入1】

4 5
##...
..1#@
432#.
...#. 

【样例输出1】

4 

【样例输入2】

4 4
#78#
.612
.543
..@. 

【样例输出2】

6 

【样例输入3】

3 2
3@
2#
1# 

【样例输出3】

-1

HINT

在这里插入图片描述

题解

\(dfs\)玄学复杂度过去了……

注:这题用\(bfs\)的时间复杂度更低,为\(O(nm4^k)\)。我只是考场上用了\(dfs\)加小小的优化卡过去了。这篇题解只是讲用\(dfs\)怎么做一个小优化,不想看的可以跳过。

这个优化就是在处理如何维护贪吃蛇的位置(因为要防止贪吃蛇吃到自己)的问题上,如果每走一步都\(O(k)\)更新一遍会比较慢(虽然在\(k<=9\)的情况下也慢不了多少),所以我们打算每次通过\(O(1)\)来操作

我们考虑为整个过程建一个时间,即贪吃蛇每走一格就时间\(+1\)秒,那么我们考虑定义一个数组\(time[i][j]\),表示格子\([i,j]\)什么时候是空的。

我们不妨将贪吃蛇看成很多段,每段是一格长,那么贪吃蛇的头就是第一段,尾就是最后一段。

那么当游戏开始时,贪吃蛇的最后一段(尾部)的格子肯定至少在\(1\)秒后才是空的,贪吃蛇的倒数第二段的格子肯定至少在\(2\)秒后才是空的(第\(1\)秒后,倒数第一段走到第\(1\)秒前倒数第二段的格子,第\(2\)秒后,倒数第一段离开)……贪吃蛇的第\(i\)段的格子肯定至少在\(k-i+1\)秒后才是空的……贪吃蛇的第\(1\)段的格子肯定至少在\(k\)秒后才是空的。(\(k\)的解释见题面)

对于每当贪吃蛇走一步维护\(time\)的问题,我们发现只有贪吃蛇的头走到的格子的\(time\)才会有变化,那么我们考虑,假设当前时间为\(t\)秒,贪吃蛇的头下一步要走到\([xi,yi]\)

因为当前时间为\(t\)秒,贪吃蛇走到下一个格子需要\(1\)秒,然后等贪吃蛇完全经过需要\(k\)秒,所以\(time[xi][yi]=t+1+k\)秒,那么我们就能在\(O(1)\)的时间内维护贪吃蛇位置的变化了。

最后完整代码加注释如下:

#include<bits/stdc++.h>
 
#define N 20
#define K 15
#define INF 0x7fffffff
 
using namespace std;
 
struct data
{
    int x,y,s;
};
 
int n,k,m,edx,edy,ans=INF,tim[N][N],snax[N],snay[N];
//edx、edy为终点的坐标
//snax[]、snay[]为贪吃蛇的每一段的坐标
int fx[]={-1,0,1,0},fy[]={0,1,0,-1};
char ch[N][N];
bool vis[N][N];//是否走过
 
bool check(int x,int y)
{
    return x<1||x>n||y<1||y>m;
}
 
void dfs(int x,int y,int s,int t)
{
    if(x==edx&&y==edy)
    {
        ans=min(ans,s);
        return;
    }
    if(s>=ans)return;
    for(int i=0;i<4;i++)
    {
        int xi=x+fx[i],yi=y+fy[i];
        if(vis[xi][yi]||check(xi,yi)||ch[xi][yi]=='#'||tim[xi][yi]-t>1)continue;
        int tt=tim[xi][yi];
        tim[xi][yi]=t+k+1;//time值更新
        vis[xi][yi]=true;//记录贪吃蛇来过这个位置,防止死循环
        dfs(xi,yi,s+1,t+1);
        vis[xi][yi]=false;//记得改回来哦
        tim[xi][yi]=tt;
    }
}
 
void bfs()
{
    queue<data>q;
    q.push((data){snax[1],snay[1],0});
    while(!q.empty()&&(q.front().x!=edx||q.front().y!=edy))
    {
        data now=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int xi=now.x+fx[i],yi=now.y+fy[i];
            if(vis[xi][yi]||check(xi,yi)||ch[xi][yi]=='#')continue; 
            vis[xi][yi]=true;
            q.push((data){xi,yi,now.s+1});
        }
    }
    if(q.empty())puts("-1");
    else printf("%d\n",q.front().s);
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch[i]+1);
        for(int j=1;j<=m;j++)
        {
            if(ch[i][j]=='@')
            {
                edx=i,edy=j;
            }
             
            if('1'<=ch[i][j]&&ch[i][j]<='9')
            {
                snax[ch[i][j]-'0']=i,snay[ch[i][j]-'0']=j;
                k=max(k,ch[i][j]-'0');
            }
        }
    }
    bool flag=true;
    for(int i=0;i<4;i++)//特判终点的四周被堵住了
    {
        int xi=edx+fx[i];
        int yi=edy+fy[i];
        if(!check(xi,yi)&&ch[xi][yi]!='#')
        {
            flag=false;
            break;
        }
    }
    if(flag)
    {
        puts("-1");
        return 0;
    }
    if(k==1)//特判k=1用bfs更快
    {
        bfs();
        return 0;
    }
    for(int i=1;i<=k;i++)
        tim[snax[i]][snay[i]]=k-i+1;//第0秒时的time
    dfs(snax[1],snay[1],0,0);
    if(ans!=INF)printf("%d\n",ans);
    else puts("-1");
    return 0;
}

总结

用时间复杂度高的算法时,要么想另一种做法,要么手造大数据测试并丧心病狂地卡常。

posted @ 2022-10-30 10:08  ez_lcw  阅读(116)  评论(0编辑  收藏  举报