-
题目描述
标题:全球变暖
你有一张某海域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)初学打基础时最好不要看别人的东西。当穷思竭力后可以查阅大佬们的思路。不能养成不劳而获、不动脑筋的习惯;也不能一昧地闭门造车、闭关锁国。