再探搜索

目录

dfs与bfs 

A*搜索:

IDA*算法:


      从起点出发,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不了就回退,此种路径搜索策略就称为“深度优先搜索”,简称“深搜”。

dfs与bfs 

题目:城堡问题

         右图是一个城堡的地形图。请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分割成m×n(m≤50,ns50)个方块,每个方块可以有0~4面墙。

输入
程序从标准输入设备读入数据。
          第一行是两个整数,分别是南北向、东西向的方块数。
         在接下来的输入行里,每个方块用一个数字(0Sp≤50)描述。用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。
输入的数据保证城堡至少有两个房间。
输出
         城堡的房间数、城堡中最大房间所包括的方块数。
        结果显示在标准输出设备上。

解题思路

  1. 把方块看作是节点,相邻两个方块之间如果没有墙,则在方块之间连一条边,这样城堡就能转换成一个图。
  2. 求房间个数,实际上就是在求图中有多少个极大连通子图。
  3. 一个连通子图,往里头加任何一个图里的其他点,就会变得不连通,那么这个连通子图就是极大连通子图。


对每一个房间,深度优先搜索,从而给这个房间能够到达的所有位置染色。最后统计一共用了几种颜色,以及每种颜色的数量。
比如
1 1 2 2 3 3 3

1 1 1 2 3 4 3

1 1 1 5 3 5 3

1 5 5 5 5 5 3
从而一共有5个房间,最大的房间(1)占据9个格子

 代码如下:

import java.util.Scanner;

public class 城堡问题 {

	static int r, c;// 行列
	static int[][] room;
	static int[][] color;
	static int roommaxarea = 0, roomnum = 0, roomarea = 0;

	// 第room[i][k]个格子
	static void dfs(int i, int k) {
		if (color[i][k] != 0) {
			return;
		}
		roomarea++;
		color[i][k] = roomnum;
		// 是否包含西墙
		if ((room[i][k] & 1) == 0)
			dfs(i, k - 1);
		if ((room[i][k] & 2) == 0)// 北
			dfs(i - 1, k);
		if ((room[i][k] & 4) == 0)// 东
			dfs(i, k + 1);
		if ((room[i][k] & 8) == 0)// 南
			dfs(i + 1, k);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		r = sc.nextInt();
		c = sc.nextInt();
		room = new int[r + 1][c + 1];
		color = new int[r + 1][c + 1];
		for (int i = 1; i < r + 1; i++) {
			for (int j = 1; j < c + 1; j++) {
				room[i][j] = sc.nextInt();
			}
		}
		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				if (color[i][j] == 0) {
					roomnum++;
					roomarea = 0;
					dfs(i, j);
					roommaxarea = Math.max(roommaxarea, roomarea);
				}
			}
		}
		System.out.println(roomnum);
		System.out.println(roommaxarea);
	}

}

题目:踩方格

有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:

 请问:如果允许在方格矩阵上走n步(n<=20),共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。

  • 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
  • 走过的格子立即塌陷无法再走第二次;
  • 只能向北、东、西三个方向走;

思路:递归

从(i,j)出发,走n步的方案数,等于以下三项之和:

  1. 从(i+1,j)出发,走n-1步的方案数。前提:(i+1,j)还没走过
  2. 从(i,j+1)出发,走n-1步的方案数。前提:(i,j+1)还没走过
  3. 从(i,j-1)出发,走n-1步的方案数。 前提:(i,j-1)还没走过

代码如下:

package 深搜;

public class 踩方格 {

	static int[][] vis = new int[30][50];

	static int ways(int i, int j, int n) {
		if (n == 0)
			return 1;
		vis[i][j] = 1;
		int num = 0;
		if (vis[i][j - 1] == 0)
			num += ways(i, j - 1, n - 1);
		if (vis[i][j + 1] == 0)
			num += ways(i, j + 1, n - 1);
		if (vis[i + 1][j] == 0)
			num += ways(i + 1, j, n - 1);
		vis[i][j]=0;
		return num;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

A*搜索

可以理解为广搜的一种剪枝优化,较快得到最优解。这个算法是给广搜的状态一个估价参数,然后以估价参数为优先级用优先队列代替队列。

启发式搜索算法(A算法)
在BFS算法中,若对每个状态n都设定估价函数f(n)=g(n)+h(n),并且每次从队列中选节点进行扩展时,都选取f值最小的节点,则该搜索算法为启发式搜索算法,又称A算法。

  • g(n) : 从起始状态到当前状态n的代价
  • h(n) : 从当前状态n到目标状态的估计代价

算法:

       1.首先把起始位置点加入到一个称为“open List”的列表,在寻路的过程中,目前,我们可以认为open List这个列表会存放许多待测试的点,这些点是通往目标点的关键,以后会逐渐往里面添加更多的测试点,同时,为了效率考虑,通常这个列表是个已经排序的列表。

  2.如果open List列表不为空,则重复以下工作:

  (1)找出open List中通往目标点代价最小的点作为当前点;

  (2)把当前点放入一个称为close List的列表;

  (3)对当前点周围的4个点每个进行处理(这里是限制了斜向的移动),如果该点是可以通过并且该点不在close List列表中,则处理如下;

  (4)如果该点正好是目标点,则把当前点作为该点的父节点,并退出循环,设置已经找到路径标记;

  (5)如果该点也不在open List中,则计算该节点到目标节点的代价,把当前点作为该点的父节点,并把该节点添加到open List中;

  (6)如果该点已经在open List中了,则比较该点和当前点通往目标点的代价,如果当前点的代价更小,则把当前点作为该点的父节点,同时,重新计算该点通往目标点的代价,并把open List重新排序;

  3.完成以上循环后,如果已经找到路径,则从目标点开始,依次查找每个节点的父节点,直到找到开始点,这样就形成了一条路径。


题目:k短路

首先反向存图,跑最短路,求出所有点到终点的最短距离,即所有点到b点的最短距离。

然后对原图从起点开始进行广搜,用优先队列维护状态。第k个到达终点的状态就是k短路

代码如下:


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

public class k短路 {

	static int n, m, k, a, b;
	static int maxn = (int) 1e9;
	static int[] dis;
	static LinkedList<Node>[] list;
	static LinkedList<Node>[] wk;
	static boolean f;

	static void init() {
		list = new LinkedList[n + 1];
		for (int i = 1; i < n + 1; i++) {
			list[i] = new LinkedList<Node>();
		}
		wk = new LinkedList[n + 1];
		for (int i = 1; i < n + 1; i++) {
			wk[i] = new LinkedList<Node>();
		}
		dis = new int[n + 1];
	}

	// spfa跑到b的最短路
	static void spfa() {

		Queue<Integer> q = new LinkedList<>();
		boolean[] vis = new boolean[n + 1];
		Arrays.fill(dis, maxn);
		q.offer(b);
		vis[b] = true;
		dis[b] = 0;
		int u = 0;
		while (!q.isEmpty()) {
			u = q.poll();
			vis[u] = false;

			for (int j = 0; j < list[u].size(); j++) {
				int v = list[u].get(j).v;
				int w = list[u].get(j).w;
				if (dis[v] > dis[u] + w) {
					dis[v] = dis[u] + w;
					if (!vis[v]) {
						q.offer(v);
					}
				}
			}
		}
	}

	// a-->b Astar算法
	static void Astar(int s, int t) {
		PriorityQueue<Data> q = new PriorityQueue<Data>();
		Data st = new Data();
		st.now = s;
		st.pas = 0;
		st.value = dis[s];
		st.route.add(s);
		int cnt = 0;
		q.offer(st);

		LinkedList<Integer> vec;
		while (!q.isEmpty()) {
			Data u = q.poll();
			if (u.now == t) {
				cnt++;
				if (cnt == k) {
					f = true;
					for (int i = 0; i < u.route.size(); i++) {
						if (i == u.route.size() - 1) {
							System.out.println(u.route.get(i));
						} else {
							System.out.print(u.route.get(i) + "-");
						}
					}
					return;
				}
			}
			// 广搜
			for (int j = 0; j < wk[u.now].size(); j++) {
				int v = wk[u.now].get(j).v;
				int w = wk[u.now].get(j).w;
				vec = u.route;
				boolean vis = false;// 记录是否重复经过
				for (int i = 0, sz = vec.size(); i < sz; i++) {// 记录是否重复经过
					if (vec.get(i) == v) {
						vis = true;
						break;
					}
				}
				if (vis)
					continue;
				Data nx = new Data();
				nx.now = v;
				nx.pas = u.pas + w;// 当前的点已经走过的距离
				nx.value = nx.pas + dis[v];// 当前的点已经走过的距离和要到达目标需要走的距离
				nx.route.addAll(u.route);
				nx.route.add(v);
				q.offer(nx);
			}

		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		k = sc.nextInt();
		a = sc.nextInt();
		b = sc.nextInt();
		init();
		int u = 0, v = 0, w = 0;
		for (int i = 0; i < m; i++) {
			u = sc.nextInt();
			v = sc.nextInt();
			w = sc.nextInt();
			wk[u].add(new Node(v, w));
			list[v].add(new Node(u, w));
		}
		spfa();
		Astar(a, b);
		if (!f) {
			System.out.println("No");
		}
	}

}

class Node {
	int v, w;

	public Node(int v, int w) {
		this.v = v;
		this.w = w;
	}
}

class Data implements Comparable<Data> {
	// 当前位置,走过的距离,s->now->t总距离,走的步骤
	int now, pas, value;
	LinkedList<Integer> route = new LinkedList<Integer>();

	// 估价函数ff
	@Override
	public int compareTo(Data o) {
		// TODO Auto-generated method stub
		// 首先按照f排序
		if (value < o.value) {
			return -1;
		}
		if (value > o.value) {
			return 1;
		}
		// f一样按照字典序排序
		if (value == o.value) {
			int size = Math.min(route.size(), o.route.size());
			for (int i = 0; i < size; i++) {
				if (route.get(i) < o.route.get(i)) {
					return -1;
				}
				if (route.get(i) > o.route.get(i)) {
					return 1;
				}
			}
		}
		return route.size() - o.route.size();
	}

}

同样是搜索迷宫 bfs与Astar:

package Astar_IDAstar;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;

public class 迷宫 {

	static int n = 8, m = 7;
	static char[][] map = new char[n][m];
	static int[][] dir = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
	static boolean[][] vis = new boolean[n][m];

	static double dis(int x1, int y1, int x2, int y2) {
		int h1 = x1 - x2;
		int h2 = y1 - y2;
		return Math.sqrt(h1 * h1 + h2 * h2);
	}

	static boolean in(int x, int y) {
		return x >= 0 && x < n && y >= 0 && y < m;
	}

	static void Astar() {
		boolean[][] hasopen = new boolean[n][m];// open表的记录
		boolean[][] hasclose = new boolean[n][m];// close表的记录
		PriorityQueue<No> q = new PriorityQueue<No>();// open列表
		LinkedList<No> q2 = new LinkedList<No>();// close列表
		double d = dis(0, 0, n - 1, m - 1);
		q.offer(new No(0, 0, 0, d));
		hasopen[0][0] = true;
		double d2 = 0;
		No u;
		int cnt = 0;
		while (!q.isEmpty()) {
			u = q.poll();
			hasopen[u.x][u.y] = false;
			q2.offer(u);// 加入close表
			hasclose[u.x][u.y] = true;
			cnt++;
//			System.out.println(u.x + "  " + u.y);
			if (u.x == n - 1 && u.y == m - 1) {
				System.out.println(u.x + "   " + u.y + " Astar迭代了 " + cnt);
				break;
			}

			for (int i = 0; i < 4; i++) {
				int tx = u.x + dir[i][0];
				int ty = u.y + dir[i][1];
				// 如果该点不在close表中
				if (in(tx, ty) && map[tx][ty] != '1' && !hasclose[tx][ty]) {
					d2 = dis(tx, ty, 0, 0);
					d = dis(tx, ty, n - 1, m - 1);
					// 如果该点不在open表中
					if (!hasopen[tx][ty]) {
						q.offer(new No(tx, ty, u.pass + 1, d2 + d));
					} else {// 如果在open中
						for (No no : q) {
							if (no.x == tx && no.y == ty) {
								if (no.f > d2 + d) {
									q.remove(no);
									q.offer(new No(tx, ty, u.pass + 1, d2 + d));
								}
							}
						}
					}
				}

			}
		}
	}

	static void bfs() {
		PriorityQueue<No> q = new PriorityQueue<No>();
		q.offer(new No(0, 0, 0));
		vis[0][0] = true;
		No u;
		int cnt = 0;
		while (!q.isEmpty()) {
			u = q.poll();
			cnt++;

			if (u.x == n - 1 && u.y == m - 1) {
				System.out.println(u.x + "   " + u.y + " bfs迭代了 " + cnt);
				break;
			}

			for (int i = 0; i < 4; i++) {
				int tx = u.x + dir[i][0];
				int ty = u.y + dir[i][1];
				if (in(tx, ty) && !vis[tx][ty] && map[tx][ty] != '1') {
					vis[tx][ty] = true;
					q.offer(new No(tx, ty, u.pass + 1));
				}
			}
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		map[0] = "0000101".toCharArray();
		map[1] = "0000100".toCharArray();
		map[2] = "0011110".toCharArray();
		map[3] = "0001000".toCharArray();
		map[4] = "1100001".toCharArray();
		map[5] = "1101100".toCharArray();
		map[6] = "1101100".toCharArray();
		map[7] = "1101100".toCharArray();
		bfs();
		Astar();
	}

}

class No implements Comparable<No> {
	int x, y;
	int pass;
	double f;

	public No(int x, int y, int pass) {
		this.x = x;
		this.y = y;
		this.pass = pass;
	}

	public No(int x, int y, int pass, double f) {
		this.x = x;
		this.y = y;
		this.pass = pass;
		this.f = f;
	}

	@Override
	public int compareTo(No o) {
		// TODO Auto-generated method stub
		if (f < o.f)
			return -1;
		else
			return 1;
	}
}

IDA*算法:

IDA∗就是带有迭代加深和估价函数优化的搜索。是对深搜的一种优化。

本质上只是在DFS上加上了一个估价函数。

题目:骑士精神

代码:

package Astar_IDAstar;

import java.util.Arrays;
import java.util.Scanner;

//启发式搜索  f(n)=g(n)+h(n)
public class 骑士精神 {
	static char[][] check = { { '1', '1', '1', '1', '1' }, { '0', '1', '1', '1', '1' }, { '0', '0', '*', '1', '1' },
			{ '0', '0', '0', '0', '1' }, { '0', '0', '0', '0', '0' }, };// 目标函数
	static char[][] target;
	static int[] xm = new int[] { 0, -2, -2, -1, -1, 1, 1, 2, 2 };
	static int[] ym = new int[] { 0, -1, 1, -2, 2, -2, 2, -1, 1 };
	static char[][] a;
	static int ans = 0;

	// 计算h(n)
	static int dif() {
		int ret = 0;
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (a[i][j] != check[i][j])
					ret++;
			}
		}
		return ret;
	}

	static void dfs(int dep, int maxdep, int x, int y, int f) {
		int l = dif();
		if (dep + l > 16)
			return;
		if (dep >= ans)
			return;
		if (l == 0) {
			ans = dep;
			return;
		}

		for (int i = 1; i <= 8; i++) {
			int tx = x + xm[i];
			int ty = y + ym[i];
			if (tx < 0 || tx > 4)
				continue;
			if (ty < 0 || ty > 4)
				continue;
			if (f + i != 9) {
				char t = a[x][y];
				a[x][y] = a[tx][ty];
				a[tx][ty] = t;
				dfs(dep + 1, maxdep, tx, ty, i);
				t = a[x][y];
				a[x][y] = a[tx][ty];
				a[tx][ty] = t;
			}
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		int T = sc.nextInt();
		a = new char[6][6];
		while (T-- > 0) {
			// 读入数据
			for (int i = 0; i < 5; i++) {
				a[i] = sc.next().toCharArray();
			}
			int x = 0, y = 0;
			for (int i = 0; i < 5; i++) {
				for (int j = 0; j < 5; j++) {
					if (a[i][j] == '*') {
						x = i;
						y = j;
						break;
					}
				}
			}
			ans = 25;
			dfs(0, 16, x, y, 0);
			if (ans != 25)
				System.out.println(ans);
			else {
				System.out.println(-1);
			}
		}
	}

}
发布了133 篇原创文章 · 获赞 31 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41921315/article/details/90606852