目录
题目描述
你有一张某海域 NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入描述
第一行包含一个整数 N(1≤N≤1000)。
以下 N 行 N 列代表一张海域照片。
照片保证第 1 行、第 1 列、第 N 行、第 N 列的像素都是海洋。
输出一个整数表示答案。
输入输出样例:
输入
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出
1
题目分析:(DFS)
蓝桥杯(阅读理解杯),首先要看明白题目的要求
1.给的是一个图,“.”表示海水,"#"表示陆地,根据题目要求,其实只有两种情况,海水和陆地,那么可以考虑用boolean来存放,false为海水,true为陆地
2.什么是岛屿?一块陆地也可以算一座岛屿,只要它四周都是海水,相连的陆地,周围是海水,那么也只算一块岛屿,明白了这个,再继续往下看
3.如果一个陆地 上下左右 都是陆地,那么它就肯定不会被淹没。按照这个来看,我们可以把这块陆地叫做 “山”。因为不是山的陆地,周围肯定有海水,按照题目来说,就是肯定会被淹没。
4.有一种情况(error了两次,就是因为没考虑这个),也就是一块岛屿,被淹没之后,有可能变成了两块岛屿,所以先求所有的岛屿数量,在将这些岛屿进行淹没处理,在计算剩余岛屿数量是行不通的。既然这样,那我们可以换一个思路,我们先拿到图,复制一份,将图中为山的坐标记为true,其余都是海水,然后原始的那张图就正常记录(false是海水,true是陆地),然后遇到陆地,就dfs搜索该块陆地(row,col),如果这块陆地没有和山相连,那么这块岛屿(因为要搜索与这块陆地相连的部分,也可以将这块陆地叫做岛屿,当然,如果这块陆地周围都是水,那么也可以叫做岛屿)中没有山,那么这块岛屿肯定会被沉没,那么我们就返回true,然后记录淹没数量。
5.具体代码每一步我都写有注释,如果不明白可以讨论区提问,我看到就会回~
AC代码(Java):
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
//创建两个boolean数组,用来存放海域状态(因为海域就只可能有两种状态,海水和陆地),节省空间
static boolean[][] target,result;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
//target存放海域初始状态
target = new boolean[N][N];
for(int row = 0;row<N;row++){
String str = sc.next();
char[] chs = str.toCharArray();
for(int col = 0;col<N;col++){
//如果当前位置是陆地,我们就记为true,否则记为false
if(chs[col]=='#'){
target[row][col] = true;
}else{
target[row][col] = false;
}
}
}
//result只存放海域中不会被淹没的陆地(即上下左右都是陆地),也可以叫做’山‘
result = new boolean[N][N];
for(int row = 0;row<N;row++){
for(int col = 0;col<N;col++){
//先判断该坐标是否为陆地,如果是陆地,在判断是不是'山'
if(target[row][col]){
//因为第一行,第一列,第N行,第N列都是. 也就是false,所以不用担心越界
//山的条件是 上下左右都是陆地,所以一次性写上
if( target[row-1][col] && target[row+1][col] && target[row][col-1] && target[row][col+1]){
//如果上下左右都是陆地,那么我们就记为'山',也就是true
result[row][col] = true;
}else{
//不是山的肯定会被淹没,所以直接当海水处理
result[row][col] = false;
}
}
}
}
//处理完,现在有了目标海域(target,用来计算非山的陆地数量,也就是沉没的陆地数量)和结果海域(result,记录不会沉没的陆地信息)
//我们dfs搜索目标海域,如果遇到陆地,就判断陆地里面有没有山,有山,代表这一部分的陆地都不会沉没完,也就不符合题目要求的完全沉没的岛屿
int number = 0;//记录被淹没的岛屿数量
for(int row = 0;row<N;row++){
for(int col = 0;col<N;col++){
//先判断当前坐标是否为陆地,如果为陆地(true),那么就搜索
if(target[row][col]){
//以row,col为坐标起点,搜索周围相连的陆地,并把他们修改为海水
if(dfs(row,col)){
//搜索完之后,如果是true,那么代表搜索的区域都没有 山 ,那么这区域肯定是要被淹没的岛屿
number++;
}
}
}
}
//输出被淹没的岛屿数量
System.out.println(number);
sc.close();
}
/**
* 用来搜索给定坐标内的所有陆地(也就是这块岛屿),是否有山,如果有山,那么代表这块岛屿都不会被淹没,也就是返回false
* 如果这块岛屿内没有山,那么肯定会被淹没,就返回true进行计数
* */
private static boolean dfs(int row, int col) {
//默认周围没有'山'
boolean isEmpty = true;
//将当前坐标修改为海水
target[row][col] = false;
//然后判断当前坐标是不是山,如果是’山‘,那么就修改是否存在山的情况
if(result[row][col]){
isEmpty = false;
}
//不管是否存在有山的情况,都需要继续搜索周围为陆地的坐标,
//然后将他们淹没(同时判断他们是否为山,所以当前坐标为'山'那么没有直接return),
//不然后续会遍历到,并且当成一个新的岛屿
//因为第一行,第一列,第N行,第N列都是. 也就是false,所以不用担心越界
if(target[row-1][col]){
//如果上是陆地,那么就淹没这块陆地
//需要递归判断搜索的地方是不是山,如果是山,那么最终的返回结果也肯定是false;
isEmpty = isEmpty & dfs(row-1,col);
}
if(target[row+1][col]){
//下
isEmpty = isEmpty & dfs(row+1,col);
}
if(target[row][col-1]){
//左
isEmpty = isEmpty & dfs(row,col-1);
}
if(target[row][col+1]){
//右
isEmpty = isEmpty & dfs(row,col+1);
}
//返回搜索之后的结果(如果这一块搜索的区域有山,那么就肯定是false)
return isEmpty;
}
}