算法分析与设计 实验4 搜索

实验4 搜索

学号:6130116217

专业班级:计算机科学与技术165班

课程名称:算法分析与设计实验

一、全球变暖

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

在这里插入图片描述
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

1、分析

  • 此题为自选的典型搜索问题,可以采用dfs或者bfs,这里用dfs。
  • 思路:n表示像素宽度,char[][] map存储淹没前岛屿,char[][] map1存储淹没后岛屿。boolean[][] vis记录某个坐标是否已访问。
  • 从每个点开始dfs,记录开始岛屿数目start。处理淹没,再次重复上述dfs搜索过程记录淹没后岛屿数量end。两者差值即为结果。
  • 注意:岛屿淹没后可能变多,结果可以是负数。

2、代码

package JavaA.s9;

import java.io.*;
import java.util.*;

public class 全球变暖 {
	static Scanner in = new Scanner(System.in);
	static int n;
	static char[][] map = new char[1010][1010];
	static char[][] map1 = new char[1010][1010];
	static String temp;
	static int start, end;
	static boolean[][] vis = new boolean[1010][1010];

	public static void main(String[] args) throws ParseException, FileNotFoundException {
		Scanner in = new Scanner(new File("src/JavaA/s9/8.txt"));
		n = in.nextInt();
		System.out.println("N= " + n);
		for (int i = 1; i <= n; ++i) {// 读入岛屿
			temp = in.next();
			for (int j = 1; j <= n; ++j) {
				map[i][j] = map1[i][j] = temp.charAt(j - 1);
			}
		}

		// 计算初始岛屿数目start
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (!vis[i][j] && map[i][j] == '#') {
					dfs(i, j);
					start++;
				}
			}
		}
		// 处理淹没,只要四个相邻位置有'.'就会被淹没
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (map1[i][j] == '#' && (map1[i - 1][j] == '.' || map1[i + 1][j] == '.' || map1[i][j - 1] == '.'
						|| map1[i][j + 1] == '.'))
					map[i][j] = '.';

			}
		}
		// 计算淹没后岛屿数量end
		for (int i = 1; i <= n; ++i)
			Arrays.fill(vis[i], false);
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (!vis[i][j] && map[i][j] == '#') {
					dfs(i, j);
					end++;
				}
			}
		}
		// 输出结果
		System.out.println("淹没前数量:" + start + "\t\t淹没后数量:" + end);
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				System.out.print(map1[i][j]);
			}
			System.out.print("\t\t\t");
			for (int j = 1; j <= n; ++j) {
				System.out.print(map[i][j]);
			}
			System.out.println();
		}
		// 淹没
		System.out.println("淹没的数量: " + (start - end));
	}

	static void dfs(int x, int y) {
		if (x < 1 || x > n || y < 1 || y > n)// 越界
			return;
		if (vis[x][y] || map[x][y] != '#')// 已访问过或者是陆地,退出
			return;
		vis[x][y] = true;
		dfs(x - 1, y);// 向四个方向延伸
		dfs(x + 1, y);
		dfs(x, y - 1);
		dfs(x, y + 1);// 每个点不能两次访问,无需回溯
	}
}

3、测试

  • 下面为3组数据运行结果截图
测试1

在这里插入图片描述

测试2(结果为负数)

在这里插入图片描述

测试3(结果为负数)

在这里插入图片描述

二、高斯八皇后问题

1、分析

  • 此题为课本中9.1.1节的高斯八皇后问题。
  • 课本中使用的是枚举法,我用带回溯的dfs(深度优先搜索)重写了一遍。
  • 思路是:用一个数组int b[]记录每行皇后所在列数,boolean vis[]记录某一列是否已存在皇后。选定某一个皇后前都检查是否在同一列、同一斜线是否已存在皇后。这样,就不会走错误的路,保证每一个选定的皇后都是合法的。
  • 经过测试,dfs方法比枚举法速度快得多。

2、修改

  • 下面列出课本中的c语言代码改写成java,和dfs的代码。
(1).枚举
package 算法设计;

import java.io.*;
import java.util.*;

public class 高斯八皇后 {
	static Scanner in = new Scanner(System.in);
	static int a, i, j, k, s, t, x, y;
	static int[] f = new int[10];
	static int[] g = new int[10];

	public static void main(String[] args) throws FileNotFoundException {
		long time = System.currentTimeMillis();
		Scanner in = new Scanner(new File("src/算法设计/5.txt"));
		System.out.println("高斯八皇后问题的解为: ");
		// 步长为9枚举8位数
		for (a = 12345678; a <= 87654321; a = a + 9) { 
			y = a;
			for (i = 1; i <= 8; i++)
				f[i] = 0;
			for (k = 1; k <= 8; k++) {
				x = y % 10;
				f[x]++;
				g[k] = x;
				y = y / 10; // 分离a各个数字并用f,g数组统计
			}
			for (t = 0, i = 1; i <= 8; i++)
				if (f[i] != 1) {
					t = 1; // 数字1--8出现不为1次,返回
					break;
				}
			if (t == 1)
				continue;
			for (k = 1; k <= 7; k++)
				for (j = k + 1; j <= 8; j++)
				// 同处在45度斜线上,返回
					if (Math.abs(g[j] - g[k]) == j - k) { 
						t = 1;
						k = 7;
						break;
					}
			if (t == 1)
				continue;
			s++; // 输出8皇后问题的解
			System.out.print(a + " ");
			if (s % 6 == 0)
				System.out.println();
		}
		System.out.println();
		System.out.println("高斯八皇后问题解的个数: " + s);

		System.out.println((System.currentTimeMillis() - time) + "ms");
	}
}

(2).dfs
package 算法设计;

import java.io.*;
import java.util.*;

public class 高斯八皇后 {
	static Scanner in = new Scanner(System.in);
	static boolean[] vis = new boolean[10];// 访问标记
	static int[] b = new int[10];// 记录某一列是否已选
	static int count;// 总数

	public static void main(String[] args) throws FileNotFoundException {
		long time = System.currentTimeMillis();
		Scanner in = new Scanner(new File("src/算法设计/5.txt"));
		System.out.println("高斯八皇后问题的解为: ");
		dfs(0);
		System.out.println();
		System.out.println("高斯八皇后问题解的个数: "+count);

		System.out.println((System.currentTimeMillis() - time) + "ms");
	}

	static void dfs(int n) {
		if (n == 8) {
			count++;
			for (int i = 1; i <= 8; ++i)// 打印一个解
				System.out.print(b[i]);
			System.out.print(count%6==0?"\n":" ");
			return;
		}
		for (int i = 1; i <= 8; ++i) {
			if (!vis[i]) {
				boolean flag = false;// 检测是否有统一斜线的皇后
				for (int j = 1; j <= n; ++j) {
					if (n + 1 - j == Math.abs(i - b[j]))
						flag = true;
				}
				if (!flag) {// 可以选该位置,进行dfs并回溯
					vis[i] = true;
					b[n + 1] = i;
					dfs(n + 1);
					vis[i] = false;
				}
			}
		}
	}
}

3、测试

  • 下面为两种方法运行结果截图
(1)、枚举

在这里插入图片描述

(2)、dfs

在这里插入图片描述

4、比较

  • 下面是两种方法分别运行5次所用时间记录。
测试次数枚举(ms)dfs(ms)
129523
232715
333814
430028
529715
  • 不难看出:课本上的枚举法平均用时约为300ms,而我写的dfs法平均用时约20ms。因此修改后的带回溯dfs方法效率约为课本上枚举法的15倍,这是一个非常大的改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值