冰面滑行
题目描述
有一个 N×M 的网格,每个格子是冰面 . 或岩石 #。整个网格最外圈保证全部是岩石。
玩家最初停在格子 (2,2),该格子保证是冰面。
一次操作中,玩家先选择上、下、左、右中的一个方向,然后沿该方向持续滑行,直到下一格是岩石为止。若下一格已经是岩石,则玩家原地不动并结束本次操作。
玩家可以进行任意多次操作。
一个冰面格子只要玩家能够在某次滑行中经过它,或者停在它上面,就称这个格子"可触达"。
请计算可触达的冰面格子数量。
输入格式
第一行两个整数 N,M。
接下来 N 行,每行一个长度为 M 的字符串 Si。
输出格式
输出可触达冰面格子的数量。


解题思路
这题和普通连通块不同,每个格子不能四个方向搜索,如果选定了一个方向去探索,后续格子都要以这个方向去探索,直到下一步碰到岩石,这时才可以换别的方向。
怎么搜索可达的位置呢?我们开一个队列存储可以换向的格子,对于这种格子遍历四个方向探索。选定方向后,我们可以用一个while循环单向探索直到下一个碰到岩石(后称这种格子叫临界格子)。然后我们判断临界格子是否有标记过,如果没有,则格子入队。在单向探索时,如果这个格子标记为可达的(同时也具有被访问的标记)。
最后数一下标记过的格子数量,输出答案。
AC Code
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m, p[205][205], ans=0;
char mp[205][205];
struct xy{
int x, y;
};
queue<xy>q;
int dx[5]={0, 0, 0, 1, -1};
int dy[5]={0, 1, -1, 0, 0};
signed main(){
memset(p, -1, sizeof(p));
cin>>n>>m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin>>mp[i][j];
}
}
q.push({2, 2});
p[2][2]=1;
while(!q.empty()){
int x=q.front().x, y=q.front().y;
// cout<<x<<" "<<y<<'\n';
q.pop();
for(int i = 1; i <= 4; i++){
int xx=x, yy=y;
while(xx+dx[i]>=1&&xx+dx[i]<=n&&yy+dy[i]>=1&&yy+dy[i]<=m&&mp[xx+dx[i]][yy+dy[i]]=='.'){
p[xx][yy]=1;
xx+=dx[i];
yy+=dy[i];
}
if((xx!=x||yy!=y)&&p[xx][yy]==-1){
p[xx][yy]=1;
q.push({xx, yy});
}
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
// cout<<p[i][j]<<'\t';
if(p[i][j]==1)ans++;
}
// cout<<'\n';
}
cout<<ans;
}