[广度优先搜索]01迷宫

题目大意


 题目描述

有一个仅由数字01组成的n×n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入格式 

1行为两个正整数n,m。
下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。
接下来m行,每行2个用空格分隔的正整数i,j,对应了迷宫中第i行第j列的一个格子,询问从这一格开始能移动到多少格。

输出格式

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

输入输出样例

输入 #1

2 2
01
10
1 1
2 2

输出 #1

4
4

三份代码

#1 70pts做法(暴搜)

//3个点TLE
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
struct Pos
{
    int x,y;
};
queue <Pos> q;
int n,m,x,y,tx,ty,ans,ask_a,ask_b;
char mp[1001][1001];
bool vis[1001][1001];
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};
int bfs(int sx,int sy)
{
    q.push((Pos){sx,sy});
    vis[sx][sy]=true;
    while(!q.empty())
    {
        x=q.front().x; 
        y=q.front().y;
        q.pop();
        for(int i=0;i<4;i++)
        {
            tx=x+dx[i];
            ty=y+dy[i];
            if(tx<=0||tx>n||ty<=0||ty>n) continue;
            if(vis[tx][ty]==true||mp[tx][ty]==mp[x][y]) continue;
            ans++;
            q.push((Pos){tx,ty});
            vis[tx][ty]=true;
        }
    }
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>mp[i][j];
    for(int i=1;i<=m;i++)
    {
        memset(vis,false,sizeof(vis));
        ans=0;
        cin>>ask_a>>ask_b;
        cout<<bfs(ask_a,ask_b)+1<<endl;
    }
    return 0;
}

#2 70pts做法(暴搜+连通块标记)

//先输入后计算+标记方法错误
#include<iostream> #include<queue> #include<cstring> using namespace std; struct Pos { int x,y; }; queue <Pos> q,bk; int n,m,x,y,tx,ty,ans,ask_a,ask_b; char mp[1001][1001]; bool vis[1001][1001]; int book[1001][1001]; const int dx[]={1,-1,0,0}; const int dy[]={0,0,1,-1}; int bfs(int sx,int sy) { q.push((Pos){sx,sy}); bk.push((Pos){sx,sy}); vis[sx][sy]=true; while(!q.empty()) { x=q.front().x; y=q.front().y; q.pop(); for(int i=0;i<4;i++) { tx=x+dx[i]; ty=y+dy[i]; if(tx<=0||tx>n||ty<=0||ty>n) continue; if(vis[tx][ty]==true||mp[tx][ty]==mp[x][y]) continue; ans++; q.push((Pos){tx,ty}); bk.push((Pos){tx,ty}); vis[tx][ty]=true; } } while(!bk.empty()) { book[bk.front().x][bk.front().y]=ans; bk.pop(); } return ans; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>mp[i][j]; cin>>ask_a>>ask_b; cout<<bfs(ask_a,ask_b)+1<<endl; for(int i=2;i<=m;i++) { ans=0; memset(vis,false,sizeof(vis)); cin>>ask_a>>ask_b; if(book[ask_a][ask_b]!=0) cout<<book[ask_a][ask_b]+1<<endl; else cout<<bfs(ask_a,ask_b)+1<<endl; } return 0; }

#3 AC做法(暴搜+连通块标记+先计算后访问)

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
struct Pos
{
    int x,y;
};
queue <Pos> q,bk; //STL队列
int n,m,x,y,tx,ty,ans,ask_a,ask_b;
char mp[1001][1001]; //存储地图
bool vis[1001][1001]; //存储访问情况
int book[1001][1001]; //存储连通计算后的值
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1}; //四个方向
void bfs(int sx,int sy)
{
    q.push((Pos){sx,sy});
    bk.push((Pos){sx,sy});
    vis[sx][sy]=true;
    while(!q.empty())
    {
        x=q.front().x; 
        y=q.front().y;
        q.pop();
        for(int i=0;i<4;i++)
        {
            tx=x+dx[i];
            ty=y+dy[i];
            if(tx<=0||tx>n||ty<=0||ty>n) continue;
            if(vis[tx][ty]==true||mp[tx][ty]==mp[x][y]||book[tx][ty]!=0) continue;
            ans++;
            q.push((Pos){tx,ty});
            bk.push((Pos){tx,ty});
            vis[tx][ty]=true;
        }
    }
    while(!bk.empty())
    {
        book[bk.front().x][bk.front().y]=ans;
        bk.pop();
    }
    ans=0; //每次都要清零
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>mp[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(book[i][j]==0) bfs(i,j); //先计算后访问
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&ask_a,&ask_b);
        printf("%d\n",book[ask_a][ask_b]+1); 
    }
    return 0;
}

总结

本题坑点

#1 数据规模大。
#2 答案ans的清零。
#3 连通块的几录。
#4 重复的搜索。
#5 计算、访问的先后顺序。

本题难度 普及/提高-

上一篇:dfs关于按钮问题(flip游戏POJ1753)以及和bfs的区别+板子


下一篇:java写bfs