hiho一下 第156周 岛屿

题目1 : 岛屿

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

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

1. 照片中海岛的数目

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

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

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

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

上图所示的照片中一共有4座岛屿;其中3座面积为4,一座面积为2,所以不同面积的岛屿数目是2;有两座形状都是"####",所以形状不同的岛屿数目为3。

输入

第一行包含两个整数:N 和 M,(1 ≤ NM ≤ 50),表示照片的行数和列数。

以下一个 N * M 的矩阵,表示表示海域的照片。

输出

输出3个整数,依次是照片中海岛的数目、面积不同的海岛数目和形状不同的海岛数目。

样例输入
5 7
.####..  
.....#.  
####.#.  
.....#.  
..##.#.  
样例输出
4 2 3
参考了论坛的题目分析
https://hihocoder.com/discuss/question/4580

《岛屿》题目分析

本题是一道非常经典的2D地图上的搜索问题,类似的问题还包括走迷宫、求房间数目等。

这道题需要我们求出三个数值,一是海岛数目,二是面积不同的海岛数目,三是形状不同的海岛数目。

第一个小问题是最基本的搜索问题。

一般我们可以从上到下、从左到右扫描,直到发现一个没有处理过的'#'。然后从这个'#'开始沿着4个方向扩展,把连在一起的'#'都找出来。这些连在一起的'#'就组成一个岛屿。

伪代码如下,当然用bfs代替dfs也是没问题的:

for i = [0 .. N):
    for j = [0 .. M):
        if NOT visited[i][j] AND grid[i][j] == '#':
            dfs(i, j)

dfs(i, j):
    int dx[4] = {-1, 1, 0, 0}
    int dy[4] = {0, 0, -1, 1}
    visited[i][j] = true
    for d = [0 .. 4):
        int _x = i + dx[d]
        int _y = j + dy[d]
        if (_x, _y) is inside the boarder AND NOT visited[_x][_y] AND grid[_x][_y] == '#':
            dfs(_x, _y)

第二个小问题很容易就可以在第一个小问题的基础上求得。我们只需在dfs/bfs找岛屿的过程中保存一下'#'的数目即可。

第三个小问题判断形状相同,我们可以用相对位置来判断。

以样例数据为例,第一行的"####"对应的坐标(行从上到下,列从左到右)依次是(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中保持按上下左右的顺序扩展陆地)那么对于两个形状相同的岛屿,我们求出的坐标序列恰好就是一一对应的。

如果我们还不放心,可以将坐标序列重新排序之后,再选择第一个坐标为基准点,求出相对位置序列。


import java.util.*;
public class Main{
    public static void main(String[] args) {
		// TODO Auto-generated method stub


		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int m=sc.nextInt();
		sc.nextLine();
		char[][] c=new char[n][m];
		String[] s=new String[n];
		for(int i=0;i<n;i++){
			s[i]=sc.nextLine();
			for(int j=0;j<m;j++){
				c[i][j]=s[i].charAt(j);
			}
		}
		sc.close();
//		for(int i=0;i<n;i++){
//			for(int j=0;j<m;j++){
//				System.out.print(c[i][j]);
//			}
//			System.out.println();
//		}
		helper(c);
	}
	
	public static void helper(char[][] c){
		Set<Integer> set1=new HashSet<>();
		Set<List<Integer>> set2=new HashSet<>();
		int count=0;
		int n=c.length;
		int m=c[0].length;
		boolean[][] flag=new boolean[n][m];
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				if(!flag[i][j] && c[i][j]=='#'){
					int area=0;
					List<Integer> list1=new ArrayList<>();
					List<Integer> list2=new ArrayList<>();
					area=dfs(c,i,j,flag,list1);
					set1.add(area);
					count++;
					int min=Integer.MAX_VALUE;
					for(int k=0;k<list1.size();k++){
						min=Math.min(min, list1.get(k));
					}
					for(int k=0;k<list1.size();k++){
						list2.add(list1.get(k)-min);
					}
					set2.add(list2);
				}
			}
		}
		System.out.println(count+" "+set1.size()+" "+set2.size());
		
//		for(int i:set1)System.out.println(i);
//		for(List<Integer> l:set2){
//			System.out.println(l);
//		}
		
	}
	public static int dfs(char[][] c,int i,int j,boolean[][] flag,List<Integer> list)
	{
		int area=0;
		int n=flag.length;
		int m=flag[0].length;
		int dx[]={-1,1,0,0};
		int dy[]={0,0,-1,1};
		flag[i][j]=true;
		area++;
		list.add(i*m+j);
		for(int v=0;v<4;v++){
			int _x=i+dx[v];
			int _y=j+dy[v];
			if(_x>=0 && _x<n && _y>=0 && _y<m && flag[_x][_y]==false && c[_x][_y]=='#' ){
				area+=dfs(c,_x,_y,flag,list);
			}
		}
//		System.out.println(area);
		return area;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值