【炸僵尸】bfs爆内存,dfs超时的问题

1070: 炸僵尸
时间限制: 1 Sec 内存限制: 128 MB
题目描述
妈妈得知小星星成功地解决了买玩具难题,奖励他去看电影《生化危机 6》,小星星看着看着就替女主角担心起来了,因为她要对付那么多的僵尸怪物,小星星恨不得扔颗炸弹消除可恶的僵尸们,他脑袋里开始构思出这样的场景:

在这里插入图片描述

在一个 N 行 M 列单元格构成的地图中,去放置一个炸弹,这种炸弹威力巨大,以放置点为中心进行行列延伸炸到同行同列的僵尸,但不能穿墙。上图中可以把炸弹放置在第 3 行第 4 列,最多可以炸到 4 个僵尸,如果对地图稍加改动(如下图),在第 5 行第 4 列处加

入一个墙体,又如何呢?答案还是最多炸到 4 个僵尸,只不过最佳炸弹放置点发生了变化,应该放到第 6 行第 6 列的位置。

在这里插入图片描述

当然炸弹要靠勇敢的小星星去放,他只能在地图中朝上下左右四个方向行进(不能斜对

角移动),他不能穿墙,也不能穿越僵尸,要保证他的安全,如下图,告诉你小星星起始位置是第 2 行第 2 列,那么他的最佳放置炸弹位置应该是第 3 行第 2 列,最多炸到 2 个僵尸。

在这里插入图片描述

现在请聪明的你也一起加入到消除僵尸的队伍来。

输入

第一行四个用空格隔开的正整数表示 N,M,X,Y,分别表示 N 行 M 列的地图,小星星起始位置第 X 行,第 Y 列。

接下来 N 行 M 列用来描述地图上每个单元格,‘G’表示僵尸,‘#’表示墙体,只有‘.’

表示的单元格才是小星星能够正常行走,能够放置炸弹的单元格。(数据保证四面都是墙体,也就是第 1 行、第 N 行、第 1 列、第 M 列肯定都是墙体)。

输出

一个整数,最多能炸掉的僵尸数量。

样例输入 Copy
13 13 4 2
#############
###…GG#GGG.#
###.#G#G#G#G#
#…#…G#
#G#.###.#G#G#
#GG.GGG.#.GG#
#G#.#G#.#.#.#
##G…G…#
#G#.#G###.#G#
#…G#GGG.GG#
#G#.#G#G#.#G#
#GG.GGG#G.GG#
#############
样例输出 Copy
10
提示
100%的数据,保证 N,M<2001, X<N,Y<M

蒟蒻博主这道题写了两天。大致记录一下每一种思路。

1,(TLE)先尝试用bfs从起点开始走,之后每个点都计算所能炸到的僵尸数量,用时2900+ms,严重超时。

2,(TLE&&MLE)之后采用预处理,先用两个双重循环找出所有非墙的点能炸到的僵尸数量,存到一个二维数组中(AC代码中依旧采用该预处理,但储存方式不一样),不仅多了50ms还把内存爆了。。

3,(TLE&&MLE)然后开始各种尝试缩减内存,short int 都用上了,省的内存少的可怜。

4,(MLE)bfs不行就尝试dfs,依然保留预处理方式,用dfs遍历并更新最大值,用时100+ms合格,但内存190k左右。。。

5,(AC)博主的bfs队列用的是结构体存坐标,结构体和二维数组都存坐标就造成了浪费,以至于爆内存,便采用线性表示存储坐标,也就是结构体的下标写成x*maxn+y,遍历改用灌水法,先将能到达的地方用bool vis二维数组存储节省空间 ,最后遍历一次就可以了。

AC代码:(37240KB 701MS)

#include <iostream>
#include <queue>
using namespace std;
typedef short int si;//本题最大可能出现的数为4000,用short int 完全可以
const si maxn = 2001;
si ans,n,m;
si dx[4]={
    
    0,0,1,-1},dy[4]={
    
    1,-1,0,0};
bool vis[maxn][maxn];
struct node
{
    
    
    si x,y;//存坐标
    char t;//字符
    si num;//该坐标能炸到的僵尸数量
} s[maxn*(maxn+1)+1];//线性表示坐标,为方便处理,选择1到n不是0到n-1读入,这里稍微扩大
node now,nt;
queue<node>q;
void bfs(si x,si y)//广搜
{
    
    
    vis[x][y]=true;
    now = s[x*maxn+y];//提取对应坐标的所有信息
    q.push(now);
    while(!q.empty())
    {
    
    
        now = q.front();
        for(si i = 0;i<4;i++)
        {
    
    
            si nx = now.x + dx[i];
            si ny = now.y + dy[i];
            if(nx<1||nx>n||ny<1||ny>m||s[nx*maxn+ny].t!='.'||vis[nx][ny])continue;
            else
            {
    
    
                vis[nx][ny] = true;
                q.push(s[nx*maxn+ny]);//压入下一个坐标的所有信息
            }
        }
        q.pop();
    }
}
int main()
{
    
    
    si x,y,i,j,k;
    cin>>n>>m>>x>>y;
    for(i = 1; i<=n; i++)
        for(j = 1; j<=m; j++)
        {
    
    
            s[i*maxn+j].x = i;//横坐标
            s[i*maxn+j].y = j;//纵坐标
            cin>>s[i*maxn+j].t;//坐标对应字符
        }
    si sum=0;//下面是预处理,分横向纵向两次
    //从左往右,碰到墙就结束该次炸到的僵尸数量统计
    for(i = 1;i<=n;i++)
    {
    
    
        for(j = 1;j<=m;j++)
        {
    
    
            if(s[i*maxn+j].t!='#')//
            {
    
    
                if(s[i*maxn+j].t=='G')
                sum++;
            }
            else
            {
    
    
                for(k = j-1;k>=1&&s[i*maxn+k].t!='#';k--)//僵尸数量赋值给僵尸点已经空点(僵尸会挡路,但无妨)
                {
    
    
                    s[i*maxn+k].num = sum;
                }
                sum = 0;
            }
        }
    }
    //纵向同理
    for(j = 1;j<=m;j++)
    {
    
    
        for(i = 1;i<=n;i++)
        {
    
    
            if(s[i*maxn+j].t!='#')
            {
    
    
                if(s[i*maxn+j].t=='G')
                sum++;
            }
            else
            {
    
    
                for(k = i-1;k>=1&&s[k*maxn+j].t!='#';k--)
                {
    
    
                   s[k*maxn+j].num += sum;
                }
                sum = 0;
            }
        }
    }
    bfs(x,y);
    for(i = 1;i<=n;i++)
    {
    
    
        for(j = 1;j<=m;j++)
        {
    
    
            if(vis[i][j]&&ans<s[i*maxn+j].num)ans=s[i*maxn+j].num;//能走到并炸到更多就更新答案
        }
    }
    cout<<ans;
    return 0;
}

线性表示是看到学长在创建项目名时采用的编号112501(11月25日第1份代码)学到的,在这道题上算是抖机灵吧,感谢支持。

猜你喜欢

转载自blog.csdn.net/qq_51152918/article/details/111687217