Best First Search
A* Search
A* search algorithm selects the path that minimizes
f(n) = g(n) + h(n)
where
n
is the next node on the pathg(n)
is the cost of the path from the start node ton
h(n)
is a heuristic function that estimates the cost of the cheapest path fromn
to the goal- problem-specific
- admissible: it never overestimates the cost of reaching the goal - A* is guaranteed to return a least-cost path
- monotone/consistent: its estimate is always less than or equal to the estimated distance from any neighbouring vertex to the goal, plus the cost of reaching that neighbour - A* is guaranteed to find an optimal path without processing any node more than once and A* is equivalent to running Dijkstra’s algorithm with the reduced cost
d'(x, y) = d(x, y) + h(y) − h(x)
Shortest Path in Binary Matrix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
private static final int[][] DIRECTIONS = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {-1, 1}, {-1, -1}, {1, -1}};
private static final int MAX = 200;
public int shortestPathBinaryMatrix(int[][] grid) {
int m = grid.length, n = grid[0].length;
if (grid[m - 1][n - 1] == 1) {
return -1;
}
// x, y, g, f
Queue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[3]));
pq.offer(new int[]{0, 0, 1, 1 + Math.max(m, n)});
Map<Integer, Integer> minDist = new HashMap<>();
while (!pq.isEmpty()) {
int[] node = pq.poll();
int r = node[0], c = node[1];
if (r < 0 || r == m || c < 0 || c == n || grid[r][c] == 1) {
continue;
}
if (r == m - 1 && c == n - 1) {
return node[2];
}
if (minDist.getOrDefault(r * n + c, MAX) <= node[2]) {
continue;
}
minDist.put(r * n + c, node[2]);
for (int[] d : DIRECTIONS) {
int g = node[2] + 1;
// h(n) is a heuristic function that
// estimates the cost of the cheapest path from node to the goal
// here h(n) = diagonal distance (max norm)
int h = Math.max(Math.abs(m - 1 - r), Math.abs(n - 1 - c));
pq.offer(new int[]{r + d[0], c + d[1], g, g + h});
}
}
return -1;
}
Minimum Moves to Move a Box to Their Target Location
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
private static final int[][] DIRECTIONS = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {-1, 1}, {-1, -1}, {1, -1}};
public int minPushBox(char[][] grid) {
int m = grid.length, n = grid[0].length;
int[] start = null, box = null, target = null;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 'S') {
start = new int[]{i, j};
} else if (grid[i][j] == 'B') {
box = new int[]{i, j};
} else if (grid[i][j] == 'T') {
target = new int[]{i, j};
}
}
}
// {h, pushes, player[0], player[1], box[0], box[1]}
Queue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
int[] node = new int[]{h(box[0], box[1], target), 0, start[0], start[1], box[0], box[1]};
pq.offer(node);
Set<String> set = new HashSet<>();
while (!pq.isEmpty()) {
node = pq.poll();
// box is at the target
if (node[4] == target[0] && node[5] == target[1]) {
return node[1];
}
// set key
String key = Arrays.stream(node, 2, node.length)
.mapToObj(String::valueOf)
.collect(Collectors.joining(" - "));
if (!set.add(key)) {
continue;
}
for (int[] d : DIRECTIONS) {
// player
int pr = node[2] + d[0], pc = node[3] + d[1];
if (isBlocked(grid, pr, pc, m, n)) {
continue;
}
int[] next = null;
if (pr == node[4] && pc == node[5]) {
// player is at box
int br = node[4] + d[0], bc = node[5] + d[1];
if (isBlocked(grid, br, bc, m, n)) {
continue;
}
next = new int[]{h(br, bc, target) + node[1] + 1, node[1] + 1, pr, pc, br, bc};
} else {
// box doesn't move
next = new int[]{node[0], node[1], pr, pc, node[4], node[5]};
}
pq.offer(next);
}
}
return -1;
}
private boolean isBlocked(char[][] grid, int i, int j, int m, int n) {
return i < 0 || i == m || j < 0 || j == n || grid[i][j] == '#';
}
// Manhattan's distance
private int h(int box0, int box1, int[] target) {
return Math.abs(box0 - target[0]) + Math.abs(box1 - target[1]);
}
This post is licensed under CC BY 4.0 by the author.