题目描述
你有一张某海域NxN 像素的照片,"."表示海洋、"#"表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入描述
第一行包含一个整数N (1≤N≤1000)。
以下 N 行 N 列代表一张海域照片。
照片保证第 1 行、第 1 列、第 N行、第 N 列的像素都是海洋。、
输出一个整数表示答案。
输入输出样例
示例
输入
7 ....... .##.... .##.... ....##. ..####. ...###. .......
输出
1
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
理解题目
这个题目让我们寻找输入的图中有几个岛会被淹没,首先理解题目中对岛的定义,题目中说:上下左右"四个方向上连在一起的一片陆地组成一座岛屿 总结就是,图中所有单独存在的#组成的区域都可以被成为岛屿,例如图中的
.......
.##....
.##....
....##.
..####.
...###.
.......
其中颜色相同的就为一个岛屿,所以题目中的图中共有两个岛屿,我们初步了解的题目的题意,而题目让我们寻找会有几个岛屿会被完全淹没,那么就可以分解为,首先找到一块岛屿本身有几块陆地#,然后统计这个岛屿的陆地有几个会被淹没,题目中说,如果一块陆地#上下左右其中任何一个方向只要有海洋.,则这个陆地就会被淹没,例如题目中的图,从上到下第一个岛屿其中的陆地每一块都临海,所以第一个岛屿会被完全淹没,所以我们只需要求出这个岛屿中到底有几块陆地会被海洋吞没,然后判断这个岛屿所有被淹没的陆地数和岛屿所有的陆地数是否相等,如果相等,则这个岛屿会被完全淹没
思路总结
现在我们思路就清晰了起来,其实这是一个很典型的连通块题目,在这里我就不对连通块做太多的赘述,有兴趣的可以去查一下相关的资料,就这个题目来说,整体的思路就可以总结为,我们遍历图中的每一个节点,判断节点是否为陆地,如果是陆地我们看这个陆地是否还有其他的联通的陆地,我们搜索这个陆地联通的所有的陆地,记录他们的数量并且记录他们会被淹没的数量,最后判断数量是否相等,每次查看一个新的联通陆地节点都将这个节点关闭搜索,以防重复记录一个岛屿多次.
完整代码
我们现在清晰了所有的思路,则我们就可以着手写代码了,对于每一个#节点的查找我这里使用的是bfs,这里也可以使用dfs,大家可以尝试自己写一个dfs版本的代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
public class Main{
private static int[][] distance = new int[][] {{1,0},{-1,0},{0,1},{0,-1}};//代表上下左右四个方向
private static char[][] gra; //原始图
private static int ans = 0; //总共的被完全淹没的岛屿的数量
private static boolean[][]mark; //标记一个#是否已经被搜索过了
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(input.readLine());
gra = new char[n+1][n+1];
Node[][] allNodes = new Node[n+1][n+1];
mark = new boolean[n+1][n+1];
for(int i = 1;i<=n;i++) {
gra[i] = input.readLine().toCharArray();
}
for(int i = 1;i<=n;i++) {
for(int j = 1;j<=n;j++) {
if(gra[i][j-1]=='#'&&(!mark[i][j])) {//遍历到的这个节点是否是陆地节点,且这个节点没有被其他的岛屿处理过程中连通过
bfs(allNodes,i,j);
}
}
}
System.out.println(ans);
}
public static void bfs(Node[][] allNodes,int ii,int jj) {//广度优先搜索查找这个陆地周围的所有连通陆地
LinkedList<Node> openQueue = new LinkedList<>(); //所有与根陆地节点连通的陆地节点队列
int length = allNodes.length-1;
openQueue.add(new Node(ii, jj));
int countLand = 0;//岛屿所拥有的陆地的数量
int countSwend = 0;//岛屿会被淹没的陆地的数量
while(!openQueue.isEmpty()) {
Node nowNode = openQueue.pollFirst();
countLand++;
int x = nowNode.x;
int y = nowNode.y;
mark[x][y] = true;
boolean flag = false;//判断这个节点是否会被淹没
char type;
for(int i =0;i<4;i++) {
int xx = distance[i][0]+x;
int yy = distance[i][1]+y;
if(xx>0&&yy>0&&xx<=length&&yy<=length&&(!mark[xx][yy])) {
type = gra[xx][yy-1];
if(type=='#')//如果周围存在一个陆地节点,就把这个节点假如队列下次查看他周围的情况
openQueue.addFirst(new Node(xx,yy));
if(type=='.')//如果周围存在一个海洋节点,则这个节点一定会被淹没
flag = true;
}
}
if(flag)
countSwend++;
}
if(countLand==countSwend)
ans++;
}
static class Node{
Node(int x,int y){
this.x = x;
this.y = y;
}
int x;
int y;
}
}