Given an n x n chessboard and the initial position (x, y) of a Knight, find the probability that the Knight remains on the chessboard after making exactly k moves.
- A Knight moves in an L-shape and, from its current position, can move to any of the following 8 positions:
(±2, ±1) and (±1, ±2). - For every move, the Knight selects one of these 8 possible moves with equal probability.
- If a move takes the Knight outside the chessboard, it is considered to have left the board and cannot return.
Return the probability as a floating-point number, rounded to at most 6 decimal places.
Examples:Â
Input: n = 8, x = 0, y = 0, k = 3
Output: 0.125000
Explanation: The probability that the Knight remains on the board after exactly 3 moves is 0.125000.Input: n = 4, x = 1, y = 2, k = 4
Output: 0.024414
Explanation: The probability that the Knight remains on the board after exactly 4 moves is 0.024414.
Table of Content
[Naive Approach] Using Recursion - O(8 ^ k) Time and O(k) Space
The idea is to recursively try all 8 possible knight moves from the current position. If the knight moves outside the board, that path contributes 0 probability. If no moves are left, it contributes 1. The final probability is obtained by averaging the probabilities of all 8 moves.
Working of Approach:
- Start from the given cell.
- Recursively explore all 8 knight moves.
- Return 0 if the knight leaves the board.
- Return 1 when all k moves are completed.
- Average the probabilities of all 8 recursive calls.
#include <bits/stdc++.h>
using namespace std;
int dx[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[8] = {-1, 1, -2, 2, -2, 2, -1, 1};
// Returns probability of staying on board
double solve(int n, int k, int row, int col)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k == 0)
return 1.0;
double res = 0.0;
// Try all 8 possible moves
for (int i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i]) / 8.0;
return res;
}
double knightProbability(int n, int k, int row, int column)
{
return solve(n, k, row, column);
}
int main()
{
cout << fixed << setprecision(6) << knightProbability(4, 4, 1, 2);
return 0;
}
import java.util.Arrays;
class GFG {
static int[] dx = { -2, -2, -1, -1, 1, 1, 2, 2 };
static int[] dy = { -1, 1, -2, 2, -2, 2, -1, 1 };
// Returns probability of staying on board
public static double solve(int n, int k, int row,
int col)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k == 0)
return 1.0;
double res = 0.0;
// Try all 8 possible moves
for (int i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i])
/ 8.0;
return res;
}
public static double
knightProbability(int n, int k, int row, int column)
{
return solve(n, k, row, column);
}
public static void main(String[] args)
{
System.out.printf("%.6f%n",
knightProbability(4, 4, 1, 2));
}
}
dx = [-2, -2, -1, -1, 1, 1, 2, 2]
dy = [-1, 1, -2, 2, -2, 2, -1, 1]
# Returns probability of staying on board
def solve(n, k, row, col):
# Knight moved outside board
if row < 0 or row >= n or col < 0 or col >= n:
return 0.0
# No moves left
if k == 0:
return 1.0
res = 0.0
# Try all 8 possible moves
for i in range(8):
res += solve(n, k - 1, row + dx[i], col + dy[i]) / 8.0
return res
def knightProbability(n, k, row, column):
return solve(n, k, row, column)
if __name__ == '__main__':
print(f"{knightProbability(4, 4, 1, 2):.6f}")
using System;
class GFG {
static int[] dx = { -2, -2, -1, -1, 1, 1, 2, 2 };
static int[] dy = { -1, 1, -2, 2, -2, 2, -1, 1 };
// Returns probability of staying on board
static double solve(int n, int k, int row, int col)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k == 0)
return 1.0;
double res = 0.0;
// Try all 8 possible moves
for (int i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i])
/ 8.0;
return res;
}
public double knightProbability(int n, int k, int row,
int column)
{
return solve(n, k, row, column);
}
public static void Main()
{
GFG obj = new GFG();
Console.WriteLine(
"{0:F6}", obj.knightProbability(4, 4, 1, 2));
}
}
const dx = [ -2, -2, -1, -1, 1, 1, 2, 2 ];
const dy = [ -1, 1, -2, 2, -2, 2, -1, 1 ];
// Returns probability of staying on board
function solve(n, k, row, col)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k === 0)
return 1.0;
let res = 0.0;
// Try all 8 possible moves
for (let i = 0; i < 8; i++) {
res += solve(n, k - 1, row + dx[i], col + dy[i])
/ 8.0;
}
return res;
}
function knightProbability(n, k, row, column)
{
return solve(n, k, row, column);
}
// Driver Code
console.log(knightProbability(4, 4, 1, 2).toFixed(6));
Output
0.024414
[Better Approach] Using DP with Memoization - O(n ^ 2 * k) Time and O(n ^ 2 * k) Space
The idea is to store the probability for every state (row, column, remaining moves). If the same state is encountered again, return the stored value instead of recomputing it. This avoids repeated recursive calculations.
Working of Approach:
- Define a DP state as (row, column, remaining moves).
- If the state is already computed, return its value.
- Otherwise recursively calculate probabilities for all 8 moves.
- Store the computed probability in the DP table.
- Return the stored result.
#include <bits/stdc++.h>
using namespace std;
int dx[8] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[8] = {-1, 1, -2, 2, -2, 2, -1, 1};
// Returns probability using memoization
double solve(int n, int k, int row, int col, vector<vector<vector<double>>> &dp)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k == 0)
return 1.0;
// Already computed
if (dp[row][col][k] != -1.0)
return dp[row][col][k];
double res = 0.0;
// Explore all 8 moves
for (int i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i], dp) / 8.0;
return dp[row][col][k] = res;
}
double knightProbability(int n, int k, int row, int column)
{
vector<vector<vector<double>>> dp(n, vector<vector<double>>(n, vector<double>(k + 1, -1.0)));
return solve(n, k, row, column, dp);
}
int main()
{
cout << fixed << setprecision(6) << knightProbability(4, 4, 1, 2);
return 0;
}
import java.util.Arrays;
class GFG {
static int[] dx = { -2, -2, -1, -1, 1, 1, 2, 2 };
static int[] dy = { -1, 1, -2, 2, -2, 2, -1, 1 };
// Returns probability using memoization
static double solve(int n, int k, int row, int col,
double[][][] dp)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k == 0)
return 1.0;
// Already computed
if (dp[row][col][k] != -1.0)
return dp[row][col][k];
double res = 0.0;
// Explore all 8 moves
for (int i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i],
dp)
/ 8.0;
return dp[row][col][k] = res;
}
public double knightProbability(int n, int k, int row,
int column)
{
double[][][] dp = new double[n][n][k + 1];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int l = 0; l <= k; l++)
dp[i][j][l] = -1.0;
return solve(n, k, row, column, dp);
}
public static void main(String[] args)
{
GFG obj = new GFG();
System.out.printf(
"%.6f%n", obj.knightProbability(4, 4, 1, 2));
}
}
dx = [-2, -2, -1, -1, 1, 1, 2, 2]
dy = [-1, 1, -2, 2, -2, 2, -1, 1]
# Returns probability using memoization
def solve(n, k, row, col, dp):
# Knight moved outside board
if row < 0 or row >= n or col < 0 or col >= n:
return 0.0
# No moves left
if k == 0:
return 1.0
# Already computed
if dp[row][col][k]!= -1.0:
return dp[row][col][k]
res = 0.0
# Explore all 8 moves
for i in range(8):
res += solve(n, k - 1, row + dx[i], col + dy[i], dp) / 8.0
dp[row][col][k] = res
return res
def knightProbability(n, k, row, column):
dp = [[[-1.0 for _ in range(k + 1)] for _ in range(n)] for _ in range(n)]
return solve(n, k, row, column, dp)
if __name__ == '__main__':
print(f'{knightProbability(4, 4, 1, 2):.6f}')
using System;
class GFG {
static int[] dx = { -2, -2, -1, -1, 1, 1, 2, 2 };
static int[] dy = { -1, 1, -2, 2, -2, 2, -1, 1 };
// Returns probability using memoization
static double solve(int n, int k, int row, int col,
double[, , ] dp)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k == 0)
return 1.0;
// Already computed
if (dp[row, col, k] != -1.0)
return dp[row, col, k];
double res = 0.0;
// Explore all 8 moves
for (int i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i],
dp)
/ 8.0;
return dp[row, col, k] = res;
}
public double knightProbability(int n, int k, int row,
int column)
{
double[, , ] dp = new double[n, n, k + 1];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int l = 0; l <= k; l++)
dp[i, j, l] = -1.0;
return solve(n, k, row, column, dp);
}
public static void Main()
{
GFG obj = new GFG();
Console.WriteLine(
"{0:F6}", obj.knightProbability(4, 4, 1, 2));
}
}
const dx = [ -2, -2, -1, -1, 1, 1, 2, 2 ];
const dy = [ -1, 1, -2, 2, -2, 2, -1, 1 ];
// Returns probability using memoization
function solve(n, k, row, col, dp)
{
// Knight moved outside board
if (row < 0 || row >= n || col < 0 || col >= n)
return 0.0;
// No moves left
if (k === 0)
return 1.0;
// Already computed
if (dp[row][col][k] !== -1.0)
return dp[row][col][k];
let res = 0.0;
// Explore all 8 moves
for (let i = 0; i < 8; i++)
res += solve(n, k - 1, row + dx[i], col + dy[i], dp)
/ 8.0;
dp[row][col][k] = res;
return res;
}
function knightProbability(n, k, row, column)
{
const dp = Array.from(
{length : n},
() => Array.from({length : n},
() => Array(k + 1).fill(-1.0)));
return solve(n, k, row, column, dp);
}
// Driver Code
console.log(knightProbability(4, 4, 1, 2).toFixed(6));
Output
0.024414
[Expected Approach] Using Bottom-Up DP (Space Optimized) - O(n ^ 2 * k) Time and O(n ^ 2)
The idea is to maintain the probability of the knight being at every cell after each move. For every move, distribute the probability of each cell equally among all valid knight moves. Since only the previous move is needed to compute the current move, use two n × n matrices to save space.
Working of Approach:
- Initialize the starting cell with probability 1.
- For each move, create a new probability matrix.
- Distribute the current probability equally among all valid moves.
- Replace the previous matrix with the new one.
- Sum all probabilities after k moves.
Let us understand with an example:
Input: n = 4, x = 1, y = 2, k = 4
- Initialize the probability matrix with 1.0 at the starting cell (1, 2) and 0 elsewhere.
- In the first move, distribute the probability 1/8 to every valid knight move from (1, 2).
- Repeat this process for the remaining 3 moves, updating the probability matrix at each step.
- After 4 moves, each cell stores the probability of the knight being at that position.
- Sum the probabilities of all cells to get the final answer, 0.024414.
#include <bits/stdc++.h>
using namespace std;
double knightProbability(int n, int k, int row, int column)
{
vector<vector<double>> curr(n, vector<double>(n, 0.0));
curr[row][column] = 1.0;
vector<int> dx = {2, 2, -2, -2, 1, 1, -1, -1};
vector<int> dy = {1, -1, 1, -1, 2, -2, 2, -2};
for (int step = 0; step < k; step++)
{
vector<vector<double>> next(n, vector<double>(n, 0.0));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (curr[i][j] == 0.0)
continue;
for (int d = 0; d < 8; d++)
{
int ni = i + dx[d];
int nj = j + dy[d];
if (ni >= 0 && nj >= 0 && ni < n && nj < n)
{
// Divide probability by 8 at each step
next[ni][nj] += curr[i][j] / 8.0;
}
}
}
}
curr = move(next);
}
double totProb = 0.0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
totProb += curr[i][j];
}
}
return totProb;
}
int main()
{
cout << fixed << setprecision(6) << knightProbability(4, 4, 1, 2);
return 0;
}
import java.util.Arrays;
class GFG {
public double knightProbability(int n, int k, int row,
int column)
{
double[][] curr = new double[n][n];
curr[row][column] = 1.0;
int[] dx = { 2, 2, -2, -2, 1, 1, -1, -1 };
int[] dy = { 1, -1, 1, -1, 2, -2, 2, -2 };
for (int step = 0; step < k; step++) {
double[][] next = new double[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (curr[i][j] == 0.0)
continue;
for (int d = 0; d < 8; d++) {
int ni = i + dx[d];
int nj = j + dy[d];
if (ni >= 0 && nj >= 0 && ni < n
&& nj < n) {
// Divide probability by 8 at
// each step
next[ni][nj]
+= curr[i][j] / 8.0;
}
}
}
}
curr = next;
}
double totProb = 0.0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
totProb += curr[i][j];
}
}
return totProb;
}
public static void main(String[] args)
{
GFG obj = new GFG();
System.out.printf(
"%.6f%n", obj.knightProbability(4, 4, 1, 2));
}
}
def knightProbability(n, k, row, column):
curr = [[0.0] * n for _ in range(n)]
curr[row][column] = 1.0
dx = [2, 2, -2, -2, 1, 1, -1, -1]
dy = [1, -1, 1, -1, 2, -2, 2, -2]
for step in range(k):
next = [[0.0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
if curr[i][j] == 0.0:
continue
for d in range(8):
ni = i + dx[d]
nj = j + dy[d]
if 0 <= ni < n and 0 <= nj < n:
# Divide probability by 8 at each step
next[ni][nj] += curr[i][j] / 8.0
curr = next
totProb = sum(sum(row) for row in curr)
return totProb
if __name__ == '__main__':
print(f"{knightProbability(4, 4, 1, 2):.6f}")
using System;
class GFG {
public double knightProbability(int n, int k, int row,
int column)
{
double[, ] curr = new double[n, n];
curr[row, column] = 1.0;
int[] dx = { 2, 2, -2, -2, 1, 1, -1, -1 };
int[] dy = { 1, -1, 1, -1, 2, -2, 2, -2 };
for (int step = 0; step < k; step++) {
double[, ] next = new double[n, n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (curr[i, j] == 0.0)
continue;
for (int d = 0; d < 8; d++) {
int ni = i + dx[d];
int nj = j + dy[d];
if (ni >= 0 && nj >= 0 && ni < n
&& nj < n) {
// Divide probability by 8 at
// each step
next[ni, nj]
+= curr[i, j] / 8.0;
}
}
}
}
curr = next;
}
double totProb = 0.0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
totProb += curr[i, j];
}
}
return totProb;
}
public static void Main()
{
GFG obj = new GFG();
Console.WriteLine(
"{0:F6}", obj.knightProbability(4, 4, 1, 2));
}
}
function knightProbability(n, k, row, column)
{
let curr = Array.from({length : n},
() => Array(n).fill(0.0));
curr[row][column] = 1.0;
const dx = [ 2, 2, -2, -2, 1, 1, -1, -1 ];
const dy = [ 1, -1, 1, -1, 2, -2, 2, -2 ];
for (let step = 0; step < k; step++) {
let next = Array.from({length : n},
() => Array(n).fill(0.0));
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (curr[i][j] === 0.0)
continue;
for (let d = 0; d < 8; d++) {
let ni = i + dx[d];
let nj = j + dy[d];
if (ni >= 0 && nj >= 0 && ni < n
&& nj < n) {
// Divide probability by 8 at each
// step
next[ni][nj] += curr[i][j] / 8.0;
}
}
}
}
curr = next;
}
let totProb = 0.0;
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
totProb += curr[i][j];
}
}
return totProb;
}
// Driver Code
console.log(knightProbability(4, 4, 1, 2).toFixed(6));
Output
0.024414