@author 夜枫
题目:
Given a satellite image of some sea region, your task is to count:
-
The number of islands in this region
-
The number of islands having different area in this region
-
The number of islands having different shape in this region
The image is illustrated as below. Sea is represented by “.” and land is represented by ‘#’. An island consists of all connected land in 4 (up, down, left and right) directions.
.####…
…#.
####.#.
…#.
…##.#.
There are 4 islands in the image above. Three of them have an area of 4 and one has an area of 2, so the number of islands having different area is 2. Two of them share the same shape “####” so the number of islands having different shape is 3.
Input
The first line contains 2 integers N and M (1 ≤ N, M ≤ 50).
Then follows an N * M matrix which is the image of the sea region.
Output
Output 3 numbers which are the number of islands, the number of islands having different area and the number of islands having different shape in this region.
翻译:
描述
给你一张某一海域卫星照片,你需要统计:
-
照片中海岛的数目
-
照片中面积不同的海岛数目
-
照片中形状不同的海岛数目
其中海域的照片如下,".“表示海洋,”#"表示陆地。在"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。
.####…
…#.
####.#.
…#.
…##.#.
上图所示的照片中一共有4座岛屿;其中3座面积为4,一座面积为2,所以不同面积的岛屿数目是2;有两座形状都是"####",所以形状不同的岛屿数目为3。
输入
第一行包含两个整数:N 和 M,(1 ≤ N, M ≤ 50),表示照片的行数和列数。
以下一个 N * M 的矩阵,表示表示海域的照片。
输出
输出3个整数,依次是照片中海岛的数目、面积不同的海岛数目和形状不同的海岛数目。
样例输入
5 7
.####…
…#.
####.#.
…#.
…##.#.
样例输出
4 2 3
计算岛屿的面积和个数都是比较容易的,利用bfs或者dfs就可以,
例如:dfs(x,y) 如果坐标在地图中,且不为海水,则返回周边的面积和自己的面积,并且让该坐标变为海水标签(这个步骤是为了其他dfs不需要重复dfs)
只需要按照地图顺序,把map[i][j]是陆地的(陆地的第一个坐标),对其dfs(x0,y0),就可以得到该岛屿的面积,个数+1即可。
本题难点:计算岛屿的形状,根据测试发现,这个形状的意思,不包括旋转,不包括缩放,也就是从地图上,需要完全一致才算
思路1:从第一个坐标开始,dfs的时候保存其运动的轨迹(岛屿的其他像素点),比如向左走是陆地,则路径+=“左’,其他方向同理。
最初的代码:
#include<iostream>
#include<string.h>
#include<set>
using namespace std;
#define MAX 55
int map[MAX][MAX];
int cont=0;
//string lu[MAX];
string lu;
set<int> ss;
set<string> strs;
int n,m;
int dfs(int x,int y){
if(x<0||y<0||x>=n||y>=m||map[x][y]==0)return 0;
map[x][y]=0;//避免重复
int a=dfs(x-1,y);
if(a){
lu+="S";//上下左右的缩写
}
int b=dfs(x+1,y);
if(b){
lu+="X";
}
int c=dfs(x,y-1);
if(c){
lu+="z";
}
int d=dfs(x,y+1);
if(d){
lu+="y";
}
return a+b+c+d+1;//返回面积
}
int main(){
lu="";
cin>>n>>m;
char c;
int mian;
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
cin>>c;
if(c=='.'){
map[i][j]=0;
}else if(c=='#'){
map[i][j]=1;
}
}
}
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(map[i][j]==1){
mian=dfs(i,j);//面积
strs.insert(lu);//路径,set容器自动去重
lu=""; //清空
cont++;//岛屿个数
ss.insert(mian);//面积 set容器自动去重
}
}
}
cout<<cont<<" "<<ss.size()<<" "<<strs.size()<<endl;
return 0;
}
/*
7 7
#######
#.....#
#.###.#
#.#.#.#
#.###.#
#.....#
#######
3 7
###.###
#.#.#.#
###.###
*/
但是 提交后答案错误,自己手动测试了很多样例都是正确的结果。
很无语只能重新思考其他出路了
于是在度娘找到该题中文版:
链接在这里
又在讨论处看到,形状的第二种思路:hiho一下第156周《岛屿》题目分析
思路2:以样例数据为例,第一行的"####"对应的坐标(行从上到下,列从左到右)依次是(0, 1)(0, 2)(0, 3)(0, 4)。如果我们以其先最上其次最左的点,也就是(0, 1)为基准的话,相对位置序列是(0, 0)(0, 1)(0, 2)(0, 3)。
同理第三行的"####"的坐标依次是(2, 0)(2, 1)(2, 2)(2, 3),其相对位置也是(0, 0)(0, 1)(0, 2)(0, 3)。两个岛屿形状相同,当且仅当它们的相对位置序列完全相同。
所以我们需要在dfs/bfs找岛屿的过程中把陆地的位置都保存下来。
值得一提的是如果我们按照确定的顺序搜索陆地,(例如以上代码中先行后列扫描第一块陆地,同时在dfs中保持按上下左右的顺序扩展陆地)那么对于两个形状相同的岛屿,我们求出的坐标序列恰好就是一一对应的。
其实就是保存每个陆地像素跟第一个像素的相对位置,差值 x-x0,y-y0
只要两个岛屿的所有相对位置都相同就是形状相同的岛屿,否则不是;
根据这思路可以有下面代码
ac代码:
#include<iostream>
#include<string.h>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
#define MAX 55
int map[MAX][MAX];
int cont=0;//岛屿个数
typedef pair<int,int>PA;//构造键值对
vector<PA>st;//记录每个岛屿的每个像素,与第一个像素的,坐标差值,存放在动态数组中,
bool comp(const PA&a ,const PA&b ){//自定义比较
if(a.first==b.first) return a.second<b.second;
else return a.first<b.first;
}
set<int> ss;//岛屿的面积,去重原理计算,多少个岛屿的面积不同
set<vector<PA>> strs;//形状 存放每个不同形状的岛屿,:利用set集合的去重原理,把每个 vector<PA>st 放进去去重,最后 strs.size()就是形状的个数
int xx=0;//作为岛屿的第一个像素坐标,不知道为啥,x0,y0这样子老是报错,所以改了名字,xx,yx就通过了
int yx=0;
int n,m;//n*m的地图, 0到n-1
int dfs(int x,int y){
if(x<0||y<0||x>=n||y>=m||map[x][y]==0)return 0;
map[x][y]=0;//避免重复,提高效率
st.push_back(PA(x-xx,y-yx));
return dfs(x-1,y)+dfs(x+1,y)+dfs(x,y-1)+dfs(x,y+1)+1;//自己和周边的面积
}
int main(){
cin>>n>>m;
char c;
int mian;
memset(map,0,sizeof(map)); //初始化地图
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
cin>>c;
if(c=='.')
map[i][j]=0;
else if(c=='#')
map[i][j]=1;
}
}
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(map[i][j]==1){
xx=i;yx=j;//岛屿的第一个像素
mian=dfs(i,j);//返回面积
sort(st.begin(),st.end(),comp);//根据每个像素与第一个像素的坐标差值排序
strs.insert(st);//把得到st(形状)放入set容器
cont++;//岛屿数量+1
ss.insert(mian);//面积放入容器去重
st.clear();//清空形状
}
}
}
cout<<cont<<" "<<ss.size()<<" "<<strs.size()<<endl;
return 0;
}
/*
7 7
#######
#.....#
#.###.#
#.#.#.#
#.###.#
#.....#
#######
3 7
###.###
#.#.#.#
###.###
6 6
......
.##...
..#...
....#.
....##
......
*/