搜索—全球变暖
[蓝桥杯 2018 省 AB] 全球变暖
题目描述
你有一张某海域 \(N \times N\) 像素的照片,. 表示海洋、 # 表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中 "上下左右" 四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 \(2\) 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入格式
第一行包含一个整数 \(N\)。\((1 \le N \le 1000)\)。
以下 \(N\) 行 \(N\) 列代表一张海域照片。
照片保证第 \(1\) 行、第 \(1\) 列、第 \(N\) 行、第 \(N\) 列的像素都是海洋。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
7
.......
.##....
.##....
....##.
..####.
...###.
.......
样例输出 #1
1
提示
时限 1 秒, 256M。蓝桥杯 2018 年第九届省赛
分析
仔细看题,发现求解的基本步骤就是:找到所有岛屿,再筛选出会被淹没的岛屿。查找所有岛屿其实就是找连通块问题,判断连通性有三种方法:DFS、BFS、并查集。在此重点介绍DFS、简要介绍BFS。
DFS问题的细节思路:因为照片反映了一个二维数组,所以我们可以先对一个起始坐标进行岛屿的查找,同时利用dfs找到该岛屿的周边坐标,也就是输入一次坐标后,要将该坐标周围的坐标(岛屿内)全部查找一遍,为了避免重复查找,就把查找过的坐标进行标记,当进行下一次坐标输入时,先检查是否查找过该坐标。那么进入dfs后要想知道该坐标所在岛屿是否沉没,就需要判断该坐标上下左右四个方向是否为‘#’,是的话就标记该岛屿未沉没,一座岛屿内只要被标记一次就可以代表。这样我们每次输入坐标后只需要看其是否被标记,统计未被标记的次数即可。
代码实现-DFS
#include <bits/stdc++.h>
using namespace std;
const int N = 1005; int n;
char pic[N][N];
int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};//表示四个方向
bool pass[N][N];//用于标记是否查找过
bool sign;//用于标记是否未淹没
void dfs(int x,int y)
{
pass[x][y] = 1;
if(pic[x-1][y]=='#'&&pic[x][y-1]=='#'&&pic[x+1][y]=='#'&&pic[x][y+1]=='#')
sign = 1;
for(int i = 0; i < 4; i++)//对该坐标周围的坐标进行扩展查找
{
int next_x = x + dir[i][0];
int next_y = y + dir[i][1];
if(!pass[next_x][next_y]&&pic[next_x][next_y]=='#')//因为是找岛屿,所以需要‘#’且是未被标记的
dfs(next_x,next_y);
}
}
int main()
{
cin>>n;int ans = 0;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= n;j ++)
cin>>pic[i][j];
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= n;j ++)
if(!pass[i][j]&&pic[i][j]=='#')
{
sign = 0;//每搜完一次岛屿都要更新一次,假设岛屿沉没
dfs(i,j);
if( !sign )
ans++;
}
cout<<ans;
return 0;
}
代码实现-BFS
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
char mp[N][N];
int vis[N][N];
int d[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}}; //四个方向
int flag;
void bfs(int x, int y) {
queue<pair<int, int>> q;
q.push({x, y});
vis[x][y] = 1; //标记这个'#'被搜过
while (q.size()) {
pair<int, int> t = q.front();
q.pop();
int tx = t.first, ty = t.second;
if( mp[tx][ty+1]=='#' && mp[tx][ty-1]=='#' &&
mp[tx+1][ty]=='#' && mp[tx-1][ty]=='#' )
flag = 1; //上下左右都是陆地,不会淹没
for (int i = 0; i < 4; i++) { //扩展(tx,ty)的4个邻居
int nx = tx + d[i][0], ny = ty + d[i][1];
if(vis[nx][ny]==0 && mp[nx][ny]=='#'){ //把陆地放进队列
vis[nx][ny] = 1; //注意:这一句必不可少
q.push({nx, ny});
}
}
}
}
int main() {
int n; cin >> n;
for (int i = 0; i < n; i++) cin >> mp[i];
int ans = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (mp[i][j] == '#' && vis[i][j]==0) {
flag = 0;
bfs(i, j);
if(flag == 0) ans++; //这个岛全部被淹,统计岛的数量
}
cout << ans << endl;
return 0;
}
BFS主要是对当前坐标展开横向查找,先把第一个坐标位置的上下左右都查找完再查找下一个;DFS主要是找到一个坐标的一个方向进行查找再查找这个方向的方向,属于纵向查找,其他代码内容实现相同。

浙公网安备 33010602011771号