全球变暖---第九届蓝桥杯省赛题目九

  • 题目描述

标题:全球变暖

你有一张某海域NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:

. . .  . . .  .
.## .  . . .
.## .  . . .
. . .  . ##.
. .# ###.
. ..  ###.
. ..   . . . .

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。  

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会
被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),
它就会被淹没。  

例如上图中的海域未来会变成如下样子:

.......
.......
.......
.......
....#..
.......
.......

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。  

【输入格式】
第一行包含一个整数N。  (1 <= N <= 1000)  
以下N行N列代表一张海域照片。  

照片保证第1行、第1列、第N行、第N列的像素都是海洋。  

【输出格式】
一个整数表示答案。

【输入样例】
7
.......
.##....
.##....
....##.
..####.
...###.
.......

【输出样例】
1  

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。

提交程序时,注意选择所期望的语言类型和编译器类型。

  • 初步解决,注意:为了简单化,博主私自假设:边界外全部是海洋。 

#include<iostream>
#include<cstdio>
using namespace std;
char map[3][1005][1005];//map[2]用来给map[1]搭把手。 
int num;
void dfs(int d, int x, int y){
	//触碰边界返回。 
	if(x < 0||x >= num||y < 0||y >= num||map[d][x][y] != '#'){
	//需要加上 map[d][x][y]!='#',否则每次调用函数都会遍历一遍所有的数据。
	//很危险! 
		return;
	}
	
	if(map[d][x][y] == '#'){
		map[d][x][y] = '.';
	}
	
	dfs(d, x - 1, y);
	dfs(d, x + 1, y);
	dfs(d, x, y - 1);
	dfs(d, x, y + 1); 
	return;
}
int main()
{
	//数据输入。
	 scanf("%d", &num);
	 getchar();
	 //cin>>num;
	 for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		scanf("%c", &map[0][i][j]);
	 		//cin>>map[0][i][j]; 
	 		map[1][i][j] = map[0][i][j];
	 		map[2][i][j] = map[0][i][j];
	 	}
	 	getchar();
	 }
	 int FormerIlandNum = 0;
	 //先计算图中包含多少个岛屿。FormerIlandNum记录岛屿个数。 
	 for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		if(map[0][i][j] == '#'){
	 			FormerIlandNum++; 
	 			dfs(0, i, j);
	 		}
	 	}
	 } 
	 
	 //侵蚀岛屿。
	 //此时map[0]中所有元素已经全部被替换成'.'了。所以需要使用map[1]。 
	 for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		if(map[1][i][j] == '#'){//判断是否要被侵蚀掉 
	 			if(i != 0&& j != 0){
	 				//如果陆地#的上下左右都是陆地#,就不会被侵蚀。否则就被侵蚀成海洋.。 
	 				if(map[2][i-1][j] == '#'&&map[2][i+1][j] == '#'&&map[2][i][j-1] == '#'&&map[2][i][j+1] == '#'){
				 	} else{
						map[1][i][j] = '.';
				 	}
				}
	 		}
	 	}	
	} 
	
	//计算剩余海岛数量。
	int LastIlandNum = 0;
	 //先计算图中包含多少个岛屿。FormerIlandNum记录岛屿个数。 
	for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		if(map[1][i][j] == '#'){
	 			LastIlandNum++; 
	 			dfs(1, i, j);
	 		}
	 	}
	}
	cout<<FormerIlandNum - LastIlandNum; 

	return 0;
}
  • 发现问题

    参考大佬的博客发现,实际上,上面程序中并没有考虑到一个岛屿被侵蚀后转化为多个岛屿的情况,如下图所示:

                                                                             

    为了解决这个问题,我采用的方法是另开一个二维数组专门用来为联通的岛屿做标记。最后只需要统计不同标记的个数就可以了。

  • 解题代码

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
char map[4][1005][1005];//map[2]用来给map[1]搭把手。map[3]用来给地图做标记。 
int num;
set<int> set_flag;
void dfs(int flag, int d, int x, int y){
	//触碰边界返回。 
	if(x < 0||x >= num||y < 0||y >= num||map[d][x][y] != '#'){
	//需要加上 map[d][x][y]!='#',否则每次调用函数都会遍历一遍所有的数据。
	//很危险! 
		return;
	}
	
	if(map[d][x][y] == '#'){
		map[d][x][y] = '.';
		if(flag != 0){
			map[3][x][y] = flag;//用flag来标记岛屿的类别。在算剩余岛屿数量时,
			//根据flag来判断是否是来自同一个岛屿。 实际上用set来解决了。 
		}
	}
	
	dfs(flag, d, x - 1, y);
	dfs(flag, d, x + 1, y);
	dfs(flag, d, x, y - 1);
	dfs(flag, d, x, y + 1); 
	return;
}
int main()
{
	//数据输入。
	 scanf("%d", &num);
	 getchar();
	 //cin>>num;
	 for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		scanf("%c", &map[0][i][j]);
	 		//cin>>map[0][i][j]; 
	 		map[1][i][j] = map[0][i][j];
	 		map[2][i][j] = map[0][i][j];
	 	}
	 	getchar();
	 }
	 int FormerIlandNum = 0;
	 int flag = 0;
	 //先计算图中包含多少个岛屿。FormerIlandNum记录岛屿个数。 
	 for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		if(map[0][i][j] == '#'){
	 			flag++;
	 			FormerIlandNum++; 
	 			dfs(flag, 0, i, j);
	 		}
	 	}
	 } 
	 
	 //侵蚀岛屿。
	 //此时map[0]中所有元素已经全部被替换成'.'了。所以需要使用map[1]。 
	 for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		if(map[1][i][j] == '#'){//判断是否要被侵蚀掉 
	 			if(i != 0&& j != 0){
	 				//如果陆地#的上下左右都是陆地#,就不会被侵蚀。否则就被侵蚀成海洋.。 
	 				if(map[2][i-1][j] == '#'&&map[2][i+1][j] == '#'&&map[2][i][j-1] == '#'&&map[2][i][j+1] == '#'){
				 	} else{
						map[1][i][j] = '.';
				 	}
				}
	 		}
	 	}	
	} 
	
	//计算剩余海岛数量。
	 //先计算图中包含多少个岛屿。set_flag.size()记录编号不一致的剩余岛屿的个数。 
	for(int i = 0; i < num; i++){
	 	for(int j = 0; j < num; j++){
	 		if(map[1][i][j] == '#'){
	 			set_flag.insert(map[3][i][j]); 
	 			dfs(0, 1, i, j);
	 		}
	 	}
	}
	cout<<FormerIlandNum - set_flag.size(); 

	return 0;
}
  • 思路整理与经验总结

    1.思路整理

    在本题中,用到的方法是DFS/BFS。但是题目比较绕,它问的是求未被淹没的岛屿的个数。所以我们许多同学都是先DFS/BFS计算淹没前岛屿的个数,再进行岛屿淹没操作,并计算淹没后剩余岛屿的个数;最后用淹没前的岛屿个数➖淹没后的岛屿个数,求得未被淹没的岛屿的个数。

    这种思路很好,但是没有考虑到淹没后,岛屿可能会比原来多的情况(上文中已经提到了)。

    我的解决方法是在用DFS/BFS计算淹没前岛屿的个数的时候,顺便给每个岛屿做一个标记flag,flag = 1, 2, 3, 。。。。;在计算剩余海岛个数时,把岛屿的标号装进一个set集合,利用集合元素的唯一性消除重复的标号,set.size()即为标号不同的剩余岛屿的数量。最后用淹没前的岛屿个数➖标号不同的剩余岛屿的数量,求得未被淹没的岛屿的个数。

    2.经验总结

  (1)scanf("%c", &map[0][i][j])有问题。 
    为什么?是因为回车符被收入了。 需要补充上getchar()吃掉回车符。 

  (2)初学打基础时最好不要看别人的东西。当穷思竭力后可以查阅大佬们的思路。不能养成不劳而获、不动脑筋的习惯;也不能一昧地闭门造车、闭关锁国。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值