hihoCoder太阁最新面经算法竞赛题解(5)

题目来源:

    HihoCoder1310

题目要求:

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

1. 照片中海岛的数目

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

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

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

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

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

解答:

   本题使用搜索算法就可以很容易地找到所有的岛屿。利用深度或者广度优先搜索找到每一个连续的由“#”包含的区域,同时统计每个连续区域中“#”的个数就可以得到每一个岛屿的面积。因此,题目中要求的岛屿数、面积不同的岛屿数可以直接通过搜索求得。
    本题的重点在于如何记录岛屿的形状,从而求得形状不同的岛屿数目。这里笔者设定了一种方式,将岛屿的形状特征转换为一个字符串。具体格式为:<width>,<height>:<map>。首先,我们用一个恰好可以容纳当前岛屿的矩形区域来将岛屿包围,widthheight就是该矩形的宽度和高度,<map>则是当前岛屿在该岛屿的最小矩形部分的地图数据,逐行数据连接为一个字符串。具体转换方式如下图样例:

    
    需要说明的是,在确定每个岛屿最小矩形时,有可能会包含其他岛屿的数据,此时要将其删去,然后再进行形状特征字符串的转换,如下图:
 
    通过以上方法,就可以利用字符串表征岛屿的形状特征。显然,利用这样的转换方法,如果两个岛屿的形状相同,那么它的形状特征字符串一定也相同;反之也成立,即如果两个岛屿的性状特征字符串相同,那么这两个岛屿的性状也一定相同。
    到这里,本题的主要解答思路可以总结为:首先通过搜索找到所有的岛屿,然后将这些岛屿进行排序,排序标准为:首先按照岛屿面积升序排列,当两个岛屿的面积相同时,按照形状特征字符串的字典序排序,显然,如果两个岛屿的面积不同,那么其形状也一定不同。
    排序完成后,就可以很容易地得到岛屿数目、不同面积的岛屿数目、不同形状的岛屿数目这些数据的情况了。

输入输出格式:

    输入:

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

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

输出:

输出一个整数,表示最少的机器数目。

程序代码:

package hihocoder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

/**
 * This is the ACM problem solving program for hihoCoder 1310.
 * 
 * @version 2017-04-26
 * @author Zhang Yufei.
 */
public class HihoCoder1310 {
	/**
	 * Record the information of island.
	 */
	private static class Island implements
			Comparable<Island> {
		int area;
		String shape;

		@Override
		public int compareTo(Island o) {
			if (area < o.area) {
				return -1;
			} else if (area > o.area) {
				return 1;
			} else {
				return shape.compareTo(o.shape);
			}
		}
	}

	/**
	 * Input data.
	 */
	private static int N, M;

	/**
	 * The map of the islands.
	 */
	private static char[][] map;

	/**
	 * Mark if the island has been visited.
	 */
	private static int[][] tags;

	/**
	 * Record the range in x and y coordinates of a island.
	 */
	private static int maxX, minX, maxY, minY;

	/**
	 * The area of a island;
	 */
	private static int area;

	/**
	 * The main program.
	 * 
	 * @param args
	 *            The command line parameters list.
	 */
	public static void main(String[] args) {
		// Input
		Scanner scan = new Scanner(System.in);

		N = scan.nextInt();
		M = scan.nextInt();

		map = new char[N][];
		tags = new int[N][M];

		for (int i = 0; i < N; i++) {
			map[i] = scan.next().toCharArray();
			for (int j = 0; j < M; j++) {
				tags[i][j] = -1;
			}
		}

		scan.close();

		// DFS
		List<Island> islands = new ArrayList<>();

		int cnt = 0;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				if (map[i][j] == '#' && tags[i][j] == -1) {
					cnt++;

					maxX = minX = maxY = minY = -1;
					area = 0;

					dfs(i, j, cnt);

					Island island = new Island();
					island.area = area;
					island.shape = getShape(cnt);

					islands.add(island);
				}
			}
		}

		// Compute.
		Object[] islandsArray = islands.toArray();

		Arrays.sort(islandsArray);

		int areaCnt, shapeCnt;

		areaCnt = shapeCnt = 1;
		for (int i = 1; i < islandsArray.length; i++) {
			Island cur = (Island) islandsArray[i];
			Island pre = (Island) islandsArray[i - 1];
			if (cur.area != pre.area) {
				areaCnt++;
			}
			if (!cur.shape.equals(pre.shape)) {
				shapeCnt++;
			}
//			System.out.println(cur.shape);
		}

		System.out.println(islandsArray.length + " "
				+ areaCnt + " " + shapeCnt);
	}

	/**
	 * Using DFS to find island.
	 * 
	 * @param x
	 *            The x coordinate.
	 * @param y
	 *            The y coordinate.
	 * @param tag
	 *            The tag for this island.
	 */
	private static void dfs(int x, int y, int tag) {
		tags[x][y] = tag;
		area++;

		if (maxX == -1 || maxX < x) {
			maxX = x;
		}

		if (minX == -1 || minX > x) {
			minX = x;
		}

		if (maxY == -1 || maxY < y) {
			maxY = y;
		}

		if (minY == -1 || minY > y) {
			minY = y;
		}

		if (x - 1 >= 0 && map[x - 1][y] == '#'
				&& tags[x - 1][y] == -1) {
			dfs(x - 1, y, tag);
		}

		if (x + 1 < N && map[x + 1][y] == '#'
				&& tags[x + 1][y] == -1) {
			dfs(x + 1, y, tag);
		}

		if (y - 1 >= 0 && map[x][y - 1] == '#'
				&& tags[x][y - 1] == -1) {
			dfs(x, y - 1, tag);
		}

		if (y + 1 < M && map[x][y + 1] == '#'
				&& tags[x][y + 1] == -1) {
			dfs(x, y + 1, tag);
		}
	}

	private static String getShape(int tag) {
		StringBuilder builder = new StringBuilder();
		builder.append(maxX - minX + 1);
		builder.append(",");
		builder.append(maxY - minY + 1);
		builder.append(":");
		for (int i = minX; i <= maxX; i++) {
			for (int j = minY; j <= maxY; j++) {
				if (tags[i][j] == tag) {
					builder.append('#');
				} else {
					builder.append('.');
				}
			}
		}

		return builder.toString();
	}
}


 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值