Maximum Number of Moves to Kill All Pawns solution and explanation
5 more videos on this problem
Maximum Number of Moves to Kill All Pawns | Step By Step Detailed | Leetcode 3283 | codestorywithMIKcodestorywithMIK · 1:11:10 · 5K views
A-D | Leetcode Weekly Contest 414 Editorials | Maximum Number of Moves to Kill All Pawns | AbhinavAbhinav Awasthi · 59:28 · 3K views
3283. Maximum Number of Moves to Kill All Pawns | Weekly Leetcode 414codingMohan · 38:05 · 1K views
3283. Maximum Number of Moves to Kill All Pawns (Leetcode Hard)Programming Live with Larry · 44:57 · 506 views
Leetcode Weekly Contest 414 |3283 Maximum Number of Moves to Kill All Pawns Today's Contest SolutionCodeFod · 18:44 · 117 views
3283. Maximum Number of Moves to Kill All Pawns | Weekly Contest 414 | LeetcodeAmit Choraria · 33:31 · 53 views
The problem
There is a 50 x 50 chessboard with one knight and some pawns on it. You are given two integers kx and ky where (kx, ky) denotes the position of the knight, and a 2D array positions where positions[i] = [xi, yi] denotes the position of the pawns on the chessboard.
Alice and Bob play a turn-based game, where Alice goes first. In each player's turn:
- The player selects a pawn that still exists on the board and captures it with the knight in the fewest possible moves. Note that the player can select any pawn, it might not be one that can be captured in the least number of moves.
- In the process of capturing the selected pawn, the knight may pass other pawns without capturing them. Only the selected pawn can be captured in this turn.
Alice is trying to maximize the sum of the number of moves made by both players until there are no more pawns on the board, whereas Bob tries to minimize them.
Return the maximum total number of moves made during the game that Alice can achieve, assuming both players play optimally.
Note that in one move, a chess knight has eight possible positions it can move to, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction.

Example 1:
Input: kx = 1, ky = 1, positions = [[0,0]]
Output: 4
Explanation:

The knight takes 4 moves to reach the pawn at (0, 0).
Example 2:
Input: kx = 0, ky = 2, positions = [[1,1],[2,2],[3,3]]
Output: 8
Explanation:
- Alice picks the pawn at
(2, 2)and captures it in two moves:(0, 2) -> (1, 4) -> (2, 2). - Bob picks the pawn at
(3, 3)and captures it in two moves:(2, 2) -> (4, 1) -> (3, 3). - Alice picks the pawn at
(1, 1)and captures it in four moves:(3, 3) -> (4, 1) -> (2, 2) -> (0, 3) -> (1, 1).
Example 3:
Input: kx = 0, ky = 0, positions = [[1,2],[2,4]]
Output: 3
Explanation:
- Alice picks the pawn at
(2, 4)and captures it in two moves:(0, 0) -> (1, 2) -> (2, 4). Note that the pawn at(1, 2)is not captured. - Bob picks the pawn at
(1, 2)and captures it in one move:(2, 4) -> (1, 2).
Constraints:
0 <= kx, ky <= 491 <= positions.length <= 15positions[i].length == 20 <= positions[i][0], positions[i][1] <= 49- All
positions[i]are unique. - The input is generated such that
positions[i] != [kx, ky]for all0 <= i < positions.length.
Approach overview
Problem Overview: A knight starts on a chessboard with several pawns placed on different cells. Each capture requires moving the knight using standard chess knight moves. Two players alternately decide which pawn the knight captures next. Alice tries to maximize the total number of moves while Bob tries to minimize it. The task is to compute the final number of moves if both play optimally.
Approach 1: BFS for Shortest Path Precomputation (O((P+1) * N2) time, O(N2) space)
The knight can reach a square through many paths, but the shortest path is what determines the move cost between captures. Run Breadth-First Search from the starting position and from every pawn to compute the minimum knight distance to every other pawn. BFS works well because each knight move has equal cost and the board can be treated as an unweighted graph. After this step you have a distance matrix where dist[i][j] represents the minimum moves from position i to pawn j. This preprocessing converts the board navigation problem into a smaller graph problem involving only the pawns. BFS runs in O(N2) per source on an N×N board and is implemented using a queue from Breadth-First Search.
Approach 2: Minimax with Bitmask DP (O(P * 2P) time, O(P * 2P) space)
Once distances between all relevant positions are known, the problem becomes a turn-based game over subsets of pawns. Represent the remaining pawns using a bitmask. The state can be defined as (currentPosition, mask, turn). Alice chooses the next pawn that maximizes the total distance traveled, while Bob chooses the one that minimizes it. Use a recursive minimax search with memoization to avoid recomputing states. For each remaining pawn in the mask, add the precomputed BFS distance and recurse on the new state. Memoizing by (position, mask) dramatically reduces the search space because there are at most P * 2^P unique states. This technique combines Game Theory with Bitmask DP to efficiently simulate optimal play.
Recommended for interviews: The expected solution combines BFS preprocessing with a minimax + bitmask dynamic programming search. BFS alone shows you understand shortest path on a grid, but the key insight is reducing the game to a subset DP problem. Interviewers usually look for the transition from board navigation to a compressed state graph and the use of memoization to keep the exponential search manageable.
Approach 1: Approach 1: BFS for Shortest Path
This approach involves using Breadth-First Search (BFS) to determine the minimum number of moves to capture each pawn from the knight's current position. By leveraging BFS, we can efficiently compute the shortest path on a chessboard grid (an unweighted graph scenario).
We then simulate the game where Alice and Bob take turns capturing pawns with Alice aiming to maximize and Bob aiming to minimize the total moves. However, Alice should choose the pawns in such a way as to delay Bob's choices (maximizing the advantage).
from collections import deque
KNIGHT_MOVES = [(2, 1), (2, -1), (-2, 1), (-2, -1),
(1, 2), (1, -2), (-1, 2), (-1, -2)]
class Solution:
def maxMovesToKillAllPawns(self, kx, ky, positions):
def bfs(kx, ky, tx, ty):
queue = deque([(kx, ky, 0)])
visited = set((kx, ky))
while queue:
x, y, dist = queue.popleft()
if x == tx and y == ty:
return dist
for dx, dy in KNIGHT_MOVES:
nx, ny = x + dx, y + dy
if 0 <= nx < 50 and 0 <= ny < 50 and (nx, ny) not in visited:
visited.add((nx, ny))
queue.append((nx, ny, dist + 1))
return float('inf')
distances = [bfs(kx, ky, x, y) for x, y in positions]
alice_turns, bob_turns = 0, 0
for i in range(len(positions)):
alice_idx = max(range(len(distances)), key=lambda x: distances[x])
alice_turns += distances[alice_idx]
distances[alice_idx] = -1
if i + 1 < len(positions):
bob_idx = min(range(len(distances)), key=lambda x: distances[x] if distances[x] >= 0 else float('inf'))
bob_turns += distances[bob_idx]
distances[bob_idx] = -1
return alice_turns + bob_turns
# Example usage
solution = Solution()
result = solution.maxMovesToKillAllPawns(1, 1, [[0, 0]])
print("Maximum Moves:", result)const KNIGHT_MOVES = [[2, 1], [2, -1], [-2, 1], [-2, -1],
[1, 2], [1, -2], [-1, 2], [-1, -2]];
class Solution {
bfs(kx, ky, tx, ty) {
const queue = [[kx, ky, 0]];
const visited = new Set([`${kx},${ky}`]);
while (queue.length > 0) {
const [x, y, dist] = queue.shift();
if (x === tx && y === ty) return dist;
for (const [dx, dy] of KNIGHT_MOVES) {
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < 50 && ny >= 0 && ny < 50 && !visited.has(`${nx},${ny}`)) {
visited.add(`${nx},${ny}`);
queue.push([nx, ny, dist + 1]);
}
}
}
return Infinity;
}
maxMovesToKillAllPawns(kx, ky, positions) {
const distances = positions.map(([x, y]) => this.bfs(kx, ky, x, y));
let totalMoves = 0;
for (let i = 0; i < positions.length; i++) {
const aliceIdx = distances.indexOf(Math.max(...distances));
totalMoves += distances[aliceIdx];
distances[aliceIdx] = -1; // mark as 'captured'
if (i + 1 < positions.length) {
const bobIdx = distances.indexOf(Math.min(...distances.filter(d => d >= 0)));
totalMoves += distances[bobIdx];
distances[bobIdx] = -1; // mark as 'captured'
}
}
return totalMoves;
}
}
// Example usage
const solution = new Solution();
const result = solution.maxMovesToKillAllPawns(1, 1, [[0, 0]]);
console.log("Maximum Moves:", result);The code implements a function `maxMovesToKillAllPawns` which uses BFS to find the shortest path from the knight's current location to a destination pawn.
Initially, BFS calculates minimum moves for each pawn in the positions array. Then Alice and Bob choose pawns alternately while updating a counter of total moves, respecting their respective strategies. This solution considers a positional queue, visited list, and iterates through possible knight moves.
Time Complexity: O(n * m), where n is the number of pawns and m is the size of BFS exploration on a 50 x 50 board.
Space Complexity: O(m) for BFS queue and visited set.
Approach 2: Approach 2: Minimax Algorithm with Pruning
This approach uses the Minimax algorithm with alpha-beta pruning to simulate a zero-sum game between Alice and Bob. Alice aims to maximize moves while Bob minimizes them. The Minimax tree explores possible game states, progressively building and evaluating the optimal paths.
Alpha-beta pruning reduces computational workload by skipping non-optimal branches, effectively optimizing the decision process.
import java.util.*;
public class Solution {
private final int[][] KNIGHT_MOVES = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1},
{1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
private int bfs(int kx, int ky, int tx, int ty) {
Queue<int[]> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.add(new int[]{kx, ky, 0});
visited.add(kx + "," + ky);
while (!queue.isEmpty()) {
int[] current = queue.poll();
int x = current[0], y = current[1], dist = current[2];
if (x == tx && y == ty) return dist;
for (int[] move : KNIGHT_MOVES) {
int nx = x + move[0], ny = y + move[1];
String pos = nx + "," + ny;
if (nx >= 0 && nx < 50 && ny >= 0 && ny < 50 && !visited.contains(pos)) {
visited.add(pos);
queue.add(new int[]{nx, ny, dist + 1});
}
}
}
return Integer.MAX_VALUE;
}
private int minimax(List<Integer> distances, boolean aliceTurn) {
if (distances.isEmpty()) return 0;
int result = aliceTurn ? Integer.MIN_VALUE : Integer.MAX_VALUE;
for (int i = 0; i < distances.size(); i++) {
int dist = distances.remove(i);
int subResult = dist + minimax(distances, !aliceTurn);
result = aliceTurn ? Math.max(result, subResult) : Math.min(result, subResult);
distances.add(i, dist);
}
return result;
}
public int maxMovesToKillAllPawns(int kx, int ky, int[][] positions) {
List<Integer> distances = new ArrayList<>();
for (int[] pos : positions) {
distances.add(bfs(kx, ky, pos[0], pos[1]));
}
return minimax(distances, true);
}
// Example usage
public static void main(String[] args) {
Solution solution = new Solution();
int[][] positions = {{0, 0}};
System.out.println("Maximum Moves: " + solution.maxMovesToKillAllPawns(1, 1, positions));
}
}#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <cmath>
#include <limits.h>
using namespace std;
int KNIGHT_MOVES[8][2] = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1},
{1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
int bfs(int kx, int ky, int tx, int ty) {
queue<pair<pair<int, int>, int>> q;
set<pair<int, int>> visited;
q.push({{kx, ky}, 0});
visited.insert({kx, ky});
while (!q.empty()) {
auto [xy, dist] = q.front(); q.pop();
int x = xy.first, y = xy.second;
if (x == tx && y == ty) return dist;
for (auto [dx, dy] : KNIGHT_MOVES) {
int nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < 50 && ny >= 0 && ny < 50 && visited.find({nx, ny}) == visited.end()) {
visited.insert({nx, ny});
q.push({{nx, ny}, dist + 1});
}
}
}
return INT_MAX;
}
int minimax(vector<int>& distances, bool alice_turn) {
if (distances.empty()) return 0;
int result = alice_turn ? INT_MIN : INT_MAX;
for (int i = 0; i < distances.size(); ++i) {
int dist = distances[i];
distances.erase(distances.begin() + i);
int sub_result = dist + minimax(distances, !alice_turn);
result = alice_turn ? max(result, sub_result) : min(result, sub_result);
distances.insert(distances.begin() + i, dist);
}
return result;
}
int maxMovesToKillAllPawns(int kx, int ky, vector<vector<int>>& positions) {
vector<int> distances;
for (auto& pos : positions) {
distances.push_back(bfs(kx, ky, pos[0], pos[1]));
}
return minimax(distances, true);
}
// Example usage
int main() {
vector<vector<int>> positions = {{0, 0}};
cout << "Maximum Moves: " << maxMovesToKillAllPawns(1, 1, positions) << endl;
return 0;
}This Java solution leverages Minimax with BFS for move calculations. The `bfs` searches optimum capture moves while `minimax` cycles through different action sequences, deciding each step optimally.
Time Complexity: Exponential with alpha-beta pruning for efficiency.
Space Complexity: Relies on recursion depth and move calculations.
Approach 3: BFS + State Compression + Memoization
First, we preprocess the shortest distance for each pawn to any position on the chessboard and record it in the array dist, where dist[i][x][y] represents the shortest distance for the i-th pawn to the position (x, y).
Next, we design a function dfs(last, state, k), where last represents the number of the last pawn eaten, state represents the current state of the remaining pawns, and k represents whether it is Alice's or Bob's turn. The function returns the maximum number of moves for the current turn. The answer is dfs(n, 2^n-1, 1). Here, initially, the number of the last pawn eaten is n, which is also the position of the knight.
The specific implementation of the function dfs is as follows:
- If
state = 0, it means there are no pawns left, return0; - If
k = 1, it means it is Alice's turn. We need to find a pawn such that the number of moves after eating this pawn is maximized, i.e.,dfs(i, state \oplus 2^i, k \oplus 1) + dist[last][x][y]; - If
k = 0, it means it is Bob's turn. We need to find a pawn such that the number of moves after eating this pawn is minimized, i.e.,dfs(i, state \oplus 2^i, k \oplus 1) + dist[last][x][y].
To avoid repeated calculations, we use memoization, i.e., using a hash table to record the states that have already been calculated.
The time complexity is O(n times m^2 + 2^n times n^2), and the space complexity is O(n times m^2 + 2^n times n). Here, n and m represent the number of pawns and the size of the chessboard, respectively.
func maxMoves(kx int, ky int, positions [][]int) int {
n := len(positions)
const m = 50
dx := []int{1, 1, 2, 2, -1, -1, -2, -2}
dy := []int{2, -2, 1, -1, 2, -2, 1, -1}
dist := make([][][]int, n+1)
for i := range dist {
dist[i] = make([][]int, m)
for j := range dist[i] {
dist[i][j] = make([]int, m)
for k := range dist[i][j] {
dist[i][j][k] = -1
}
}
}
for i := 0; i <= n; i++ {
x := kx
y := ky
if i < n {
x = positions[i][0]
y = positions[i][1]
}
q := [][2]int{[2]int{x, y}}
dist[i][x][y] = 0
for step := 1; len(q) > 0; step++ {
for k := len(q); k > 0; k-- {
p := q[0]
q = q[1:]
x1, y1 := p[0], p[1]
for j := 0; j < 8; j++ {
x2 := x1 + dx[j]
y2 := y1 + dy[j]
if x2 >= 0 && x2 < m && y2 >= 0 && y2 < m && dist[i][x2][y2] == -1 {
dist[i][x2][y2] = step
q = append(q, [2]int{x2, y2})
}
}
}
}
}
f := make([][][]int, n+1)
for i := range f {
f[i] = make([][]int, 1<<n)
for j := range f[i] {
f[i][j] = make([]int, 2)
for k := range f[i][j] {
f[i][j][k] = -1
}
}
}
var dfs func(last, state, k int) int
dfs = func(last, state, k int) int {
if state == 0 {
return 0
}
if f[last][state][k] != -1 {
return f[last][state][k]
}
var res int
if k == 0 {
res = math.MaxInt32
}
for i, p := range positions {
x, y := p[0], p[1]
if (state>>i)&1 == 1 {
t := dfs(i, state^(1<<i), k^1) + dist[last][x][y]
if k == 1 {
res = max(res, t)
} else {
res = min(res, t)
}
}
}
f[last][state][k] = res
return res
}
return dfs(n, (1<<n)-1, 1)
}Complexity comparison
| Approach | Complexity |
|---|---|
| Approach 1: BFS for Shortest Path | Time Complexity: O(n * m), where n is the number of pawns and m is the size of BFS exploration on a 50 x 50 board. Space Complexity: O(m) for BFS queue and visited set. |
| Approach 2: Minimax Algorithm with Pruning | Time Complexity: Exponential with alpha-beta pruning for efficiency. Space Complexity: Relies on recursion depth and move calculations. |
| BFS + State Compression + Memoization | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| BFS Shortest Path Precomputation | O((P+1) * N^2) | O(N^2) | When converting the chessboard into shortest distances between the knight and all pawns |
| Minimax with Bitmask DP | O(P * 2^P) | O(P * 2^P) | Optimal strategy simulation when players alternately maximize and minimize total moves |
Frequently asked questions
Is Maximum Number of Moves to Kill All Pawns easy or hard?
Maximum Number of Moves to Kill All Pawns is classified as a Hard problem. It combines multiple advanced techniques: BFS for shortest paths, bitmask dynamic programming for subset states, and minimax game theory for optimal decision making. Recognizing that the board movement can be precomputed with BFS is the key step that simplifies the problem.
Maximum Number of Moves to Kill All Pawns Python/Java solution
Python and Java implementations typically start by running BFS from each relevant position to compute knight distances. After building the distance matrix, a recursive minimax with memoization explores all subsets of pawns. Python often uses functools.lru_cache for DP, while Java implementations store results in a DP array indexed by position and bitmask.
How to solve Maximum Number of Moves to Kill All Pawns in O(P * 2^P)?
First run BFS from the knight's start position and from each pawn to compute all pairwise knight distances. Then represent the remaining pawns as a bitmask and run a minimax recursion. At each step choose a pawn to capture, add the BFS distance, and recurse on the new mask. Memoization ensures each (position, mask) state is computed once, giving O(P * 2^P) DP complexity.
What is the best approach for Maximum Number of Moves to Kill All Pawns?
The most effective solution combines BFS shortest-path preprocessing with a minimax search using bitmask dynamic programming. BFS computes the minimum knight distance between the start position and every pawn, and between each pair of pawns. Minimax with memoization then simulates optimal play where Alice maximizes total moves and Bob minimizes them. The DP state space is roughly O(P * 2^P).
Is Maximum Number of Moves to Kill All Pawns asked at Google/Amazon/Meta?
Problems combining BFS preprocessing with bitmask DP and minimax game strategy appear frequently in interviews at companies like Google, Amazon, and Meta. They test graph traversal, state compression, and game-theory reasoning in one problem. Candidates are expected to identify the BFS distance reduction and the DP over subsets.
What data structure is used in Maximum Number of Moves to Kill All Pawns?
The solution uses a queue for BFS traversal on the chessboard grid, arrays or matrices for storing pairwise distances, and a hash map or DP table for memoizing minimax states. Bitmasks represent subsets of remaining pawns efficiently, enabling fast state transitions in the dynamic programming search.
What is the time complexity of Maximum Number of Moves to Kill All Pawns?
The total complexity comes from two parts. BFS preprocessing takes O((P+1) * N^2) on an N×N board to compute knight distances from the start and each pawn. The minimax bitmask DP explores at most O(P * 2^P) states with memoization, making the overall solution feasible even when the number of pawns is around 15.
