蓝桥杯全球变暖

题目描述

标题:全球变暖

你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:


.##…
.##…
…##.
…####.
…###.

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:





…#…

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。

照片保证第1行、第1列、第N行、第N列的像素都是海洋。

【输出格式】
一个整数表示答案。

【输入样例】
7

.##…
.##…
…##.
…####.
…###.

【输出样例】
1

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。

java代码BFS版本

对于淹没之前的陆地,利用BFS计算有几个岛屿,模拟淹没以后再用BFS计算有几个岛屿,做差即可。

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	static int N;
	static boolean[][] data; // 标记数据是海洋还是陆地
	static boolean[][] visited; // 标记数据是否已经遍历
	static boolean[][] marked; // 标记某点未来是否会被海洋覆盖

	static int before;
	static int after;

	static Queue<Integer> queue;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		data = new boolean[N][N];
		visited = new boolean[N][N];
		marked = new boolean[N][N];
		for (int i = 0; i < N; i++) {
			String s = sc.next();
			for (int j = 0; j < N; j++) {
				if (s.charAt(j) == '#') {
					data[i][j] = true;
				}
			}
		}
		// 计算之前有多少岛屿,注意这里的边界
		for (int i = 1; i < N - 1; i++) {
			for (int j = 1; j < N - 1; j++) {
				// 该点是陆地并且未被标记,则以该点为起点bfs
				if (data[i][j] && !visited[i][j]) {
					bfs(i, j);
					before++;
				}
			}
		}
		// 模拟岛屿被海洋覆盖
		for (int i = 0; i < N; i++) {
			for (int j = 1; j < N; j++) {
				if (marked[i][j]) {
					data[i][j] = false;
				}
			}
		}
		// 恢复所有的顶点为未访问过,为接下来的遍历做准备
		for (int i = 0; i < N; i++) {
			Arrays.fill(visited[i], false);
		}
		// 计算覆盖后有多少岛屿
		for (int i = 1; i < N - 1; i++) {
			for (int j = 1; j < N - 1; j++) {
				// 该点是陆地并且为被标记,则以该点为起点遍历陆地
				if (data[i][j] && !visited[i][j]) {
					bfs(i, j);
					after++;
				}
			}
		}
		// 输出
		System.out.println(before - after);
		sc.close();
	}

	// 以data[i][j]为起点遍历连通图
	// 同时进行add操作
	private static void bfs(int i, int j) {
		queue = new PriorityQueue<Integer>();
		queue.add(Integer.valueOf(i * N + j));
		visited[i][j] = true;
		Integer integer;
		while (true) {
			integer = queue.poll();
			if (integer == null) {
				break;
			}
			i = integer.intValue() / N;
			j = integer.intValue() % N;
			marks(i, j);
			add(i - 1, j);
			add(i + 1, j);
			add(i, j - 1);
			add(i, j + 1);
		}
	}

	// 如果该点未来会被海洋覆盖,则标记该点将被覆盖
	private static void marks(int i, int j) {
		if (i - 1 >= 0 && !data[i - 1][j]) {
			marked[i][j] = true;
		}
		if (i + 1 < N && !data[i + 1][j]) {
			marked[i][j] = true;
		}
		if (j - 1 >= 0 && !data[i][j - 1]) {
			marked[i][j] = true;
		}
		if (j + 1 < N && !data[i][j + 1]) {
			marked[i][j] = true;
		}
	}

	// 如果该点未被遍历,则标记该点已经遍历,同时将该点加入queue队列中
	private static void add(int i, int j) {
		if (data[i][j]) {
			if (!visited[i][j]) {
				visited[i][j] = true;
				queue.add(i * N + j);
			}
		}
	}

}

java代码BFS版本优化

之前自己写的代码简直是又臭又长,且看如下代码:
每次bfs的时候就标注该岛屿所有点的数量以及未来会被淹没点的数量,若两者相等,说明该岛屿一定被淹没,ans++

package test;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	static int N;
	static char[][] data; // 标记数据是海洋还是陆地
	static boolean[][] visited; // 标记数据是否已经遍历
	static int ans = 0;
	static int[][] delta = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; // 标记上下左右四个方向

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		data = new char[N][]; // 注意这里第二维可以不给出具体数字,但是一定要给出括号
		visited = new boolean[N][N];
		for (int i = 0; i < N; i++) {
			data[i] = sc.next().toCharArray();
		}

		// 注意这里的下标
		for (int i = 1; i < N - 1; i++) {
			for (int j = 1; j < N - 1; j++) {
				// 该点是陆地并且未被标记,则以该点为起点bfs
				if (data[i][j] == '#' && !visited[i][j]) {
					bfs(i, j);
				}
			}
		}
		System.out.println(ans);
		sc.close();
	}

	// 遍历图
	private static void bfs(int i, int j) {
		int cnt1 = 0, cnt2 = 0; // cnt1表示以i,j为起点的岛屿有多少个#,cnt2表示以i,j为起点的岛屿将有多少个点会被海洋覆盖
		// 加入起点
		Queue<Point> queue = new LinkedList<Point>();
		queue.add(new Point(i, j));
		visited[i][j] = true;
		// 循环处理队列直至为空
		while (true) {
			Point p = queue.poll();
			if (p == null) {
				break;
			}
			cnt1++;// #数量加一
			int x = p.x;
			int y = p.y;
			boolean flag = false; // 标记当前点是否会被覆盖
			for (int k = 0; k < 4; k++) {
				int xx = x + delta[k][0];
				int yy = y + delta[k][1];
				// 如果该点没有越界
				if (xx >= 0 && yy >= 0 && xx < N && yy < N) {
					// 如果当前点是陆地并且未被访问过
					if (data[xx][yy] == '#' && !visited[xx][yy]) {
						visited[xx][yy] = true;
						queue.add(new Point(xx, yy));
					}
					// 如果当前点是海洋,那么一定会造成p被覆盖
					if (data[xx][yy] == '.') {
						flag = true;
					}
				}
			}
			// 如果p会被海洋覆盖
			if (flag) {
				cnt2++;
			}
		}
		if (cnt1 == cnt2) {
			ans++;
		}
	}

	private static class Point {
		int x;
		int y;

		public Point(int i, int j) {
			x = i;
			y = j;
		}
	}

}

java代码DFS版本

只是为了熟悉一下DFS,与上述BFS思路差不多

package test;

import java.util.Scanner;

public class Main {
	static int cnt1 = 0, cnt2 = 0; // cnt1表示以i,j为起点的岛屿有多少个#,cnt2表示以i,j为起点的岛屿将有多少个点会被海洋覆盖
	static int N;
	static char[][] data; // 标记数据是海洋还是陆地
	static boolean[][] visited; // 标记数据是否已经遍历
	static int ans = 0;
	static int[][] delta = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; // 标记上下左右四个方向

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		data = new char[N][]; // 注意这里第二维可以不给出具体数字,但是一定要给出括号
		visited = new boolean[N][N];
		for (int i = 0; i < N; i++) {
			data[i] = sc.next().toCharArray();
		}

		// 注意这里的下标
		for (int i = 1; i < N - 1; i++) {
			for (int j = 1; j < N - 1; j++) {
				// 该点是陆地并且未被标记,则以该点为起点bfs
				if (data[i][j] == '#' && !visited[i][j]) {
					// 初始化cnt
					cnt1 = cnt2 = 0;
					dfs(i, j);
					// 如果#数量等于该岛屿上将被覆盖的点
					if (cnt1 == cnt2) {
						ans++;
					}
				}
			}
		}
		System.out.println(ans);
		sc.close();
	}
	
	// 遍历图
	private static void dfs(int i, int j) {

		// 加入起点
		visited[i][j] = true;
		cnt1++;// #数量加一
		boolean flag = false;
		for (int k = 0; k < 4; k++) {
			int xx = i + delta[k][0];
			int yy = j + delta[k][1];
			// 如果该点没有越界
			if (xx >= 0 && yy >= 0 && xx < N && yy < N) {
				// 如果当前点是陆地并且未被访问过
				if (data[xx][yy] == '#' && !visited[xx][yy]) {
					visited[xx][yy] = true;
					dfs(xx, yy); // 深搜下去
				} // 如果当前点是海洋,那么一定会造成p被覆盖
				if (data[xx][yy] == '.') {
					flag = true;
				}
			}
		}
		// 如果该点会被海洋覆盖
		if (flag) {
			cnt2++;
		}
	}

	private static class Point {
		int x;
		int y;

		public Point(int i, int j) {
			x = i;
			y = j;
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值