#1310 : 岛屿 || VJ 搜索

@author 夜枫

题目:
Given a satellite image of some sea region, your task is to count:

  1. The number of islands in this region

  2. The number of islands having different area in this region

  3. 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.

翻译:

描述
给你一张某一海域卫星照片,你需要统计:

  1. 照片中海岛的数目

  2. 照片中面积不同的海岛数目

  3. 照片中形状不同的海岛数目

其中海域的照片如下,".“表示海洋,”#"表示陆地。在"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。

.####…
…#.
####.#.
…#.
…##.#.
上图所示的照片中一共有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
......
.##...
..#...
....#.
....##
......
*/ 

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值