题目描述
有一个仅由数字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
首先可以想到用普通的BFS来求解,但是这时候有30%的数据会TLE。
由题意我们可以得出一个结论,同一联通块中所有的格子所求的总数都是一样的。所以就可以得到一个剪枝,先求出联通块的数量,同一联通块的答案只需求一次即可。
所以问题便转化成为了求联通块的数量,在下面提供两个方法:
方法一:
用DFS求解。使用一个 tot作为计数,将已经遍历过的联通块标记出来。
void dfs(int x,int y){
tot++; //记录答案
tot[now][0]=x,ans[now][1]=y; //记录连通块的格子
for(int i=0;i<4;i++){ //搜索下一格
if(check(nx,ny)&&!v[nx][ny]&&a[x][y]!=a[nx][ny]){
//越界、访问、颜色判断
v[nx][ny]=true; //记录访问状态
dfs(nx,ny); //下一格
}
}
}
再在主函数中提前遍历全图,其中需要记录下同一联通块,方法见下:
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) if(!v[i][j]){ //枚举格子,没有访问过就DFS
v[i][j]=true; //这格被访问
now=0; //总数记为0
dfs(i,j); //从这格开始搜索
for(int i=1;i<=now;i++) f[ans[i][0]][ans[i][1]]=now; //依次赋值,重复使用并没有问题
}
方法二:
用BFS+并查集求解。首先将初始块作为祖先节点,将同一联通块所有其他的位置都连接到此点上,更新同一联通块所有的点的答案为祖先的值。
这里还要用到一个哈希算法的技巧:将二维数组一维化
int calc(int x,int y)
{
return (x-1)*n+y;
}
相当于将二维数组依次向后排序- -
在查询本节点的答案时,需要查询祖先节点的答案,但是祖先节点是一维的形式在并查集里存在,因此需要将它复原。
inline node ID(int k)
{
int x = k/n;
int y = k%n;
if(y==0) y = n;
else x++;
return node(x,y);
}
底下附上完整代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n,m,ans,x,y,res;
int dir[4][2] = {1,0,-1,0,0,1,0,-1};
int d[maxn][maxn];
int g[maxn][maxn];
string s;
int pre[1010050];
bool vis[maxn][maxn];
struct node{
int x;int y;
node(int x_,int y_)
{
x = x_;
y = y_;
}
};
inline node ID(int k)
{
int x = k/n;
int y = k%n;
if(y==0) y = n;
else x++;
return node(x,y);
}
int find(int x)
{
if(pre[x]==x) return x;
else return pre[x] = find(pre[x]);
}
inline int calc(int x,int y)
{
return (x-1)*n+y;
}
int bfs(int x,int y)
{
if(d[x][y]) return d[x][y];
if(pre[calc(x,y)]!=calc(x,y)){
node temp = ID(find(calc(x,y)));
d[x][y] = d[temp.x][temp.y];
return d[temp.x][temp.y];
}
int res = 1;
queue<node> q;
q.push(node(x,y));
while(!q.empty())
{
node now = q.front();
q.pop();
vis[now.x][now.y] = 1;
for(int i=0;i<4;i++)
{
int newx = now.x+dir[i][0];
int newy = now.y+dir[i][1];
if(g[newx][newy]!=g[now.x][now.y]&&newx>=1&&newx<=n&&newy>=1&&newy<=n&&!vis[newx][newy])
{
res++;
pre[calc(newx,newy)] = calc(now.x,now.y);
q.push(node(newx,newy));
vis[newx][newy] = 1;
}
}
}
d[x][y] = res;
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s;
int len = s.length();
for(int j=0;j<len;j++)
{
if(s[j]=='0') g[i][j+1] = 0;
else g[i][j+1] = 1;
pre[(i-1)*n+j+1] = (i-1)*n+j+1;
}
}
for(int j=0;j<m;j++)
{
cin>>x>>y;
cout<<bfs(x,y)<<endl;
}
return 0;
}