洛谷 P1141【BFS】+记忆化搜索

题目链接:https://www.luogu.org/problemnew/show/P1141

题目描述

有一个仅由数字 0 与 1 组成的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

说明

所有格子互相可达。

对于 20% 的数据,n10 ;

对于 40% 的数据,n50 ;

对于 50% 的数据,m5 ;

对于 60% 的数据,n100,m100 ;

对于 100% 的数据,n1000,m100000 。

解题分析:
由于这题n的范围很大,如果直接bfs的话,可能会超时,所以要加一些优化,分析题目不难得到,每次搜索得到的联通块,它们的答案都相同,所以,一次搜索后,这个联通块中所有点的答案都已经求出来了,然后将答案保存即可,下次

如果要查询这个点的答案,直接调用即可,不用再进行重复搜索。

#include<iostream>
#include<queue>
using namespace std;

const int MAXN = 1000 + 10;
const int dx[] = { 0,1,0,-1 };
const int dy[] = { 1,0,-1,0 };
struct Node {
    int x, y;
};
int G[MAXN][MAXN];
int vis[MAXN][MAXN];
int cnt[MAXN*MAXN];

int n, m;
int colors = 1;

inline bool juge(int x, int y) {             //判断是否越界
    return x >= 1 && x<=n && y >= 1 && y<=n;
}
void bfs(int r, int c) {
    queue<Node> q;
    Node now,next; now.x = r, now.y = c;
    q.push(now);
    vis[r][c] = colors; cnt[colors]++;            //color代表的是序号为color的色块,cnt记录的是序号为color的联通块的答案
    while (!q.empty())
    {
        Node u = q.front(); q.pop();
        int x = u.x, y = u.y;
        for (int i = 0; i<4; i++)
        {
            next.x = x + dx[i]; next.y = y + dy[i];
            if (juge(next.x, next.y) && (G[x][y] != G[next.x][next.y]) && vis[next.x][next.y] == 0)
            {
                vis[next.x][next.y] = colors; cnt[colors]++;
                q.push(next);
            }
        }
    }
}

int main() {
    cin >> n >> m;
    char ch;
    for (int i = 1; i<=n; i++)
        for (int j = 1; j<=n; j++) {
            cin >> ch;
            G[i][j] = ch - '0';          //注意输入的时候每行的数字是连续的,所以要用字符读入,否则一行的数字会被int当成一个数
        }
    int x, y;
    for (int i = 1; i <= m; i++) {
        cin >> x >> y;
        colors = i;                     //染色的思想,每一个联通块都染成不同的颜色,因为他们的答案都是相同的,这样可以避免重复计算
        if (vis[x][y] == 0) bfs(x, y);  //如果该色块没被标记过,则进行广搜标记
        cout << cnt[vis[x][y]] << endl;
    }
    return 0;
}

2018-06-01

猜你喜欢

转载自www.cnblogs.com/00isok/p/9123003.html