P1141 01迷宫(记忆化BFS)

本文介绍了一种解决特定迷宫问题的方法,利用记忆化搜索(DFS或BFS)来计算从任意起点出发能够到达的格子数量,并通过一次搜索实现对整个迷宫的有效探索。

题目描述

有一个仅由数字 000 与 111 组成的 n×nn \times nn×n 格迷宫。若你位于一格0上,那么你可以移动到相邻 444 格中的某一格 111 上,同样若你位于一格1上,那么你可以移动到相邻 444 格中的某一格 000 上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入输出格式

输入格式:

 

第 111 行为两个正整数 n,mn,mn,m 。

下面 nnn 行,每行 nnn 个字符,字符只可能是 000 或者 111 ,字符之间没有空格。

接下来 mmm 行,每行 222 个用空格分隔的正整数 i,ji,ji,j ,对应了迷宫中第 iii 行第 jjj 列的一个格子,询问从这一格开始能移动到多少格。

 

输出格式:

 

mmm 行,对于每个询问输出相应答案。

 

输入输出样例

输入样例#1: 复制

2 2
01
10
1 1
2 2

输出样例#1: 复制

4
4

说明

所有格子互相可达。

对于 20%20\%20% 的数据, n≤10n≤10n≤10 ;

对于 40%40\%40% 的数据, n≤50n≤50n≤50 ;

对于 50%50\%50% 的数据, m≤5m≤5m≤5 ;

对于 60%60\%60% 的数据, n≤100,m≤100n≤100,m≤100n≤100,m≤100 ;

对于 100%100\%100% 的数据, n≤1000,m≤100000n≤1000,m≤100000n≤1000,m≤100000 。


思路:本题n的范围较小,但是m的范围比较大,所以很明显要用到记忆话搜索(无论DFS或BFS)

仔细观察不难发现,从一个联通快出发,扫描到的所有联通快最终能移动到的格子数与初始联通块一样

所以在一次搜索过后,除了本身赋值外,再把路径的联通块同样赋值,即可达到记忆化的效果.


#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <cstring>
#include <queue>
#define MAXN 100000
using namespace std;
typedef long long ll;
ll n,m,a,b,ma[1005][1005],dx[4]={1,-1,0,0},dy[4]={0,0,1,-1}; //ma记录最终结果 dx dy为方向数组
bool flag[1005][1005];  //标记数组
char arr[1005][1005];   //图
struct Why       //队列结构体
{
    int x,y;
    Why(ll x1,ll y1)
    {
        x=x1;
        y=y1;
    }
};
struct W
{
    int x,y;   //记录路径
};
ll bfs()
{
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)   //遍历每一个点
    {
        if(ma[ i ][ j ] == 0)     //检测之前有没有背扫描过
        {
            W jl[1000005];  //记录数组一定要开大!不然会WA 
            jl[0].x=i;  //将本身存入路径中
            jl[0].y=j;
            flag[i][j]=1; //本身已经扫描过
            ll ans=1,tt=1;   //ans为一次扫描过后的格子数,tt为路径数
            queue<Why>A;   //结构体队列
            A.push(Why(i,j));  //将自己压入队列
            while(!A.empty())
            {
                Why temp = A.front();  //取队首
                for(int k = 0; k < 4; k++) //每个方向
                {
                    int tx = temp.x + dx[k], ty = temp.y + dy[k];
                    if(arr[ temp.x ][ temp.y ] != arr[ tx ][ ty ] && flag[ tx ][ ty ] == 0 && tx >= 1 && tx <= n && ty >= 1 && ty <= n) //如果与本身不相同并且没有扫描过且没有越界
                    {
                        flag[ tx ][ ty ] = 1; //标记
                        ans++;   //格子数+1
                        jl[ tt ].x = tx; //记录路径
                        jl[ tt ].y = ty;
                        tt++;
                        A.push(Why(tx,ty)); //压队
                    }
                }
                A.pop();  //弹出队首
            }
            for(int k = 0; k < tt; k++)
            {
                ma[ jl[k].x ][ jl[k].y ] = ans;   //将路径赋值
            }
        }
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            cin>>arr[i][j];
    bfs();
    while(m--)
    {
        cin>>a>>b;   
        cout<<ma[a][b]<<endl;  //直接输出即可,节省了很大的时间
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值