目录
从起点出发,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不了就回退,此种路径搜索策略就称为“深度优先搜索”,简称“深搜”。
dfs与bfs
题目:城堡问题
右图是一个城堡的地形图。请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分割成m×n(m≤50,ns50)个方块,每个方块可以有0~4面墙。
输入
程序从标准输入设备读入数据。
第一行是两个整数,分别是南北向、东西向的方块数。
在接下来的输入行里,每个方块用一个数字(0Sp≤50)描述。用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。
输入的数据保证城堡至少有两个房间。
输出
城堡的房间数、城堡中最大房间所包括的方块数。
结果显示在标准输出设备上。
解题思路
- 把方块看作是节点,相邻两个方块之间如果没有墙,则在方块之间连一条边,这样城堡就能转换成一个图。
- 求房间个数,实际上就是在求图中有多少个极大连通子图。
- 一个连通子图,往里头加任何一个图里的其他点,就会变得不连通,那么这个连通子图就是极大连通子图。
对每一个房间,深度优先搜索,从而给这个房间能够到达的所有位置染色。最后统计一共用了几种颜色,以及每种颜色的数量。
比如
1 1 2 2 3 3 31 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步的方案数,等于以下三项之和:
- 从(i+1,j)出发,走n-1步的方案数。前提:(i+1,j)还没走过
- 从(i,j+1)出发,走n-1步的方案数。前提:(i,j+1)还没走过
- 从(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);
}
}
}
}