搜索—全球变暖

[蓝桥杯 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主要是找到一个坐标的一个方向进行查找再查找这个方向的方向,属于纵向查找,其他代码内容实现相同。

posted @ 2023-08-04 18:24  LongDz  阅读(12)  评论(0)    收藏  举报