from collections import deque
class Solution:
def minKnightMoves(self, x: int, y: int) -> int:
# Use symmetry: only need to consider the first quadrant
x, y = abs(x), abs(y)
directions = [ (2,1), (1,2), (-1,2), (-2,1), (-2,-1), (-1,-2), (1,-2), (2,-1) ]
visited = set()
queue = deque()
queue.append((0, 0, 0)) # (current_x, current_y, moves)
visited.add((0, 0))
while queue:
cur_x, cur_y, moves = queue.popleft()
if (cur_x, cur_y) == (x, y):
return moves
for dx, dy in directions:
nx, ny = cur_x + dx, cur_y + dy
if (nx, ny) not in visited and nx >= -2 and ny >= -2:
visited.add((nx, ny))
queue.append((nx, ny, moves + 1))
return -1
#include <queue>
#include <set>
#include <utility>
#include <cstdlib>
class Solution {
public:
int minKnightMoves(int x, int y) {
x = abs(x); y = abs(y);
int dirs[8][2] = { {2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1} };
std::queue<std::tuple<int,int,int>> q;
std::set<std::pair<int,int>> visited;
q.push({0, 0, 0});
visited.insert({0, 0});
while (!q.empty()) {
auto [cx, cy, moves] = q.front(); q.pop();
if (cx == x && cy == y) return moves;
for (auto &dir : dirs) {
int nx = cx + dir[0], ny = cy + dir[1];
if (visited.count({nx, ny}) == 0 && nx >= -2 && ny >= -2) {
visited.insert({nx, ny});
q.push({nx, ny, moves + 1});
}
}
}
return -1;
}
};
import java.util.*;
class Solution {
public int minKnightMoves(int x, int y) {
x = Math.abs(x); y = Math.abs(y);
int[][] dirs = { {2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1} };
Queue<int[]> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.offer(new int[]{0, 0, 0});
visited.add("0,0");
while (!queue.isEmpty()) {
int[] curr = queue.poll();
int cx = curr[0], cy = curr[1], moves = curr[2];
if (cx == x && cy == y) return moves;
for (int[] dir : dirs) {
int nx = cx + dir[0], ny = cy + dir[1];
String key = nx + "," + ny;
if (!visited.contains(key) && nx >= -2 && ny >= -2) {
visited.add(key);
queue.offer(new int[]{nx, ny, moves + 1});
}
}
}
return -1;
}
}
/**
* @param {number} x
* @param {number} y
* @return {number}
*/
var minKnightMoves = function(x, y) {
x = Math.abs(x); y = Math.abs(y);
const dirs = [ [2,1],[1,2],[-1,2],[-2,1],[-2,-1],[-1,-2],[1,-2],[2,-1] ];
const queue = [];
const visited = new Set();
queue.push([0, 0, 0]);
visited.add('0,0');
while (queue.length) {
const [cx, cy, moves] = queue.shift();
if (cx === x && cy === y) return moves;
for (const [dx, dy] of dirs) {
const nx = cx + dx, ny = cy + dy;
const key = nx + ',' + ny;
if (!visited.has(key) && nx >= -2 && ny >= -2) {
visited.add(key);
queue.push([nx, ny, moves + 1]);
}
}
}
return -1;
};
The Minimum Knight Moves problem asks: Given two integers x
and y
representing a target position on an infinite chessboard (with the knight starting at (0, 0)
), what is the minimum number of moves required for the knight to reach (x, y)
?
x
and y
can be positive or negative integers.The first instinct might be to try all possible moves from the starting position, recursively or iteratively, until the target is reached. However, since the board is infinite, a brute-force approach would be highly inefficient and could run forever if not managed carefully.
Instead, we need a way to systematically explore the possible positions, prioritizing those that are closer to the target. This is a classic scenario for Breadth-First Search (BFS), which explores all positions that can be reached in n
moves before moving on to positions that require n+1
moves. BFS guarantees that the first time we reach the target, it will be via the shortest path.
Additionally, we can use symmetry: due to the knight's movement and the infinite board, reaching (x, y)
is equivalent to reaching (|x|, |y|)
. This reduces the search space and avoids redundant calculations.
abs(x)
and abs(y)
. This avoids redundant searches in other quadrants.This approach is both efficient and guarantees correctness.
Let's walk through an example where x = 2
and y = 1
:
(0, 0)
with 0 moves.(0, 0)
: (2, 1)
, (1, 2)
, (-1, 2)
, (-2, 1)
, (-2, -1)
, (-1, -2)
, (1, -2)
, (2, -1)
.(2, 1)
: this matches our target! So, the answer is 1 move.
For a more complex example, like (5, 5)
, BFS would continue exploring all positions reachable in 1 move, then 2 moves, and so on, until it reaches (5, 5)
, always ensuring that the first time it does so is via the shortest path.
O(N^2)
where N
is the distance to the target, because in the worst case, we may visit all positions within a square of side O(N)
. However, pruning and symmetry reduce the actual number of positions visited.
O(N^2)
due to the queue and the visited set storing all positions up to the target.
The Minimum Knight Moves problem is elegantly solved using Breadth-First Search, leveraging symmetry to minimize unnecessary computation. By exploring all possible positions in increasing order of moves and pruning redundant states, we ensure both efficiency and correctness. The key insight is recognizing the infinite, symmetric nature of the chessboard and the knight's movement, allowing us to dramatically reduce the search space and guarantee the shortest path is found.