你有一张某海域 NxNNxN 像素的照片,".“表示海洋、”#"表示陆地,如下所示:
…
.##…
.##…
…##.
…####.
…###.
…
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
…
…
…
…
…#…
…
…
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入描述
第一行包含一个整数 N\ (1 \leq N \leq 1000)N (1≤N≤1000)。
以下 NN 行 NN 列代表一张海域照片。
照片保证第 1 行、第 1 列、第 NN 行、第 NN 列的像素都是海洋。、
输出一个整数表示答案。
题目解释:所有旁边是海水的陆地格子都会被淹没。若一个陆地格子上下左右都没有海水,那么它不会被淹没
考点:flood fill。flood fill是用来计算连通块个数或属性的。
思路:使用flood fill可以统计出图上有多少个小岛。然后我们可以发现,一个小岛的最边上一圈的所有格子的数量如果等于整一个岛的格子,那么这个岛就会被淹没。 因此我们要用flood fill统计两个数值。一个是岛上最边上的格子有多少个,一个是整个岛有多少个格子。
划掉的格子由于它上下左右(有一个海水就要被淹没)有海水,因此要被淹没。
只有红色圈住的那个格子上下左右都不是海水,因此不会被淹没
flood fill代码模板:
第一步:对每一个格子进行一遍bfs,bfs了多少次代表有多少个连通块
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(!st[i][j] && g[i][j] == '#')
{
bfs(i, j, bound, total);
cnt++;
}
第二步:bfs
void bfs(int x, int y, int& bound, int& total)
{
int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};
queue<pii> q;
q.push({x, y});
st[x][y] = true;
while(!q.empty())
{
pii t = q.front();
q.pop();
循环一次代表遍历了一个像素点
//这就是一块,对块的操作不要在循环里面写
for(int i = 0; i < 4; i++)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= n) continue;
if(st[a][b]) continue;
一系列条件判断,具体问题具体分析
q.push({a, b});
st[a][b] = true;
}
对具体的属性操作
}
}
ac代码:
#include <iostream>
#include <queue>
using namespace std;
#define x first
#define y second
typedef pair<int, int> pii;
using namespace std;
const int N = 1010;
bool st[N][N];
int n;
char g[N][N];
void bfs(int x, int y, int& bound, int& total)
{
int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};
queue<pii> q;
q.push({x, y});
st[x][y] = true;
while(!q.empty())
{
pii t = q.front();
q.pop();
total++;
bool is_bound = false;
//这就是一块,对块的操作不要在循环里面写
for(int i = 0; i < 4; i++)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= n) continue;
if(st[a][b]) continue;
if(g[a][b] == '.') is_bound = true;
else
{
q.push({a, b});
st[a][b] = true;
}
}
if(is_bound == true) bound++;
}
}
int main()
{
cin >> n;
for(int i = 0; i < n; i++) cin >> g[i];
int cnt = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(!st[i][j] && g[i][j] == '#')
{
int bound = 0, total = 0;
bfs(i, j, bound, total);
st[i][j] = true;
if(bound == total) cnt++;
}
cout << cnt;
}
- 需要强调一点:对属性的操作,不要在for循环内部进行(就是那个往四个方向展开的那个循环)。
每一次把像素点从队列里面pop出来,代表的都是连通块数量+1.如果要统计联通块数量,统计while循环了多少次即可。 - 数边有多少个格子也很简单,只需在for循环里面看它旁边是否有水,如果有水,证明该格子是边界的一个格子,计数+1即可。