Talentd
FleetCode
Make and follow sheets.Sign in

The Earliest and Latest Rounds Where Players Compete solution and explanation

HardDynamic ProgrammingMemoization8 min read72.1% acceptanceUpdated 9 Apr 2026
The Earliest and Latest Rounds Where Players Compete | Detailed Intuition | Leetcode 1900 | MIKcodestorywithMIK · 58:10 · 8K viewsWatch on YouTube
9 more videos on this problem
  1. The Earliest and Latest Rounds Where Players Compete | Detailed Intuition | Leetcode 1900 | MIKcodestorywithMIK · 58:10 · 8K views
  2. The Earliest and Latest Rounds Where Players Compete | Leetcode 1900 | DP BitmaskingTechdose · 39:39 · 2K views
  3. 1900 The Earliest and Latest Rounds Where Players Compete | LeetCode | Bit Manipulation | DPR Sai Siddhu · 20:53 · 2K views
  4. LeetCode 1900. The Earliest and Latest Rounds Where Players CompeteHappy Coding · 30:26 · 1K views
  5. Daily Leetcode #1929: Jul 12, 2025 - 1900. The Earliest and Latest Rounds Where Players CompeteProgramming Live with Larry · 27:28 · 1K views
  6. The Earliest and Latest Rounds Where Players Compete | LeetCode 1900 | Leetcode potd | must do dsaCode Thoughts · 29:39 · 1K views
  7. The Earliest and Latest Rounds Where Players Compete | LeetCode 1900 - Python (2 Solutions)impoldev · 26:06 · 1K views
  8. 1900. The Earliest and Latest Rounds Where Players Compete (Leetcode Hard)Programming Live with Larry · 1:05:10 · 578 views
  9. Leetcode 1900: The DP You Didn't Know You Needed! | Recursion + Memoization + Bitmasking | Hard Study Placement · 24:42 · 547 views
  10. 1900. The Earliest and Latest Rounds Where Players Compete | Leetcode Daily - PythonLeetcode Daily · 4:13 · 528 views

The problem

There is a tournament where n players are participating. The players are standing in a single row and are numbered from 1 to n based on their initial standing position (player 1 is the first player in the row, player 2 is the second player in the row, etc.).

The tournament consists of multiple rounds (starting from round number 1). In each round, the ith player from the front of the row competes against the ith player from the end of the row, and the winner advances to the next round. When the number of players is odd for the current round, the player in the middle automatically advances to the next round.

  • For example, if the row consists of players 1, 2, 4, 6, 7
    • Player 1 competes against player 7.
    • Player 2 competes against player 6.
    • Player 4 automatically advances to the next round.

After each round is over, the winners are lined back up in the row based on the original ordering assigned to them initially (ascending order).

The players numbered firstPlayer and secondPlayer are the best in the tournament. They can win against any other player before they compete against each other. If any two other players compete against each other, either of them might win, and thus you may choose the outcome of this round.

Given the integers n, firstPlayer, and secondPlayer, return an integer array containing two values, the earliest possible round number and the latest possible round number in which these two players will compete against each other, respectively.

Example 1:

Input: n = 11, firstPlayer = 2, secondPlayer = 4
Output: [3,4]
Explanation:
One possible scenario which leads to the earliest round number:
First round: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Second round: 2, 3, 4, 5, 6, 11
Third round: 2, 3, 4
One possible scenario which leads to the latest round number:
First round: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Second round: 1, 2, 3, 4, 5, 6
Third round: 1, 2, 4
Fourth round: 2, 4

Example 2:

Input: n = 5, firstPlayer = 1, secondPlayer = 5
Output: [1,1]
Explanation: The players numbered 1 and 5 compete in the first round.
There is no way to make them compete in any other round.

Constraints:

  • 2 <= n <= 28
  • 1 <= firstPlayer < secondPlayer <= n

Approach overview

Problem Overview: You are given n players in a knockout tournament where the first player faces the last, the second faces the second-last, and so on. Two specific players always win until they face each other. The task is to compute the earliest and latest possible round where these two players can compete, considering all valid outcomes of other matches.

Approach 1: Simulation Approach (Exponential Time, Moderate Space)

This approach directly simulates tournament rounds while exploring every possible outcome for matches that do not involve the two target players. In each round, players are paired from both ends of the lineup and winners move to the next round. When neither of the tracked players participates in a match, both outcomes are explored, generating multiple possible player configurations for the next round. The process continues until the two players meet, tracking the minimum and maximum round numbers. Time complexity grows exponentially because many bracket configurations are explored, while space complexity is proportional to the number of simulated states.

Approach 2: Recursive DP with Memoization (O(n^3) time, O(n^3) space)

The optimized solution models the tournament using recursion and caches repeated states with memoization. Instead of simulating full player lists, the state is defined by (n, firstPlayer, secondPlayer). In each round, players are paired symmetrically and only the relative positions of the tracked players matter. You iterate through all possible numbers of winners that could appear before each tracked player in the next round. These combinations represent different outcomes of unrelated matches. Each resulting configuration recursively computes the earliest and latest meeting round for the reduced bracket. Storing results in a DP cache avoids recomputing identical states, reducing the complexity to roughly O(n^3) time and O(n^3) space.

This approach relies heavily on dynamic programming and recursive state transitions similar to tournament bracket DP problems. The key insight is that player identities do not matter except for the two tracked positions; only their indices relative to the bracket affect future rounds.

Recommended for interviews: The recursive DP with memoization solution is the expected approach. Brute-force simulation demonstrates understanding of the tournament mechanics, but interviewers look for the state-compression insight that reduces the problem to positions and uses DP caching to avoid exponential exploration.

Approach 1: Simulation Approach

This approach involves simulating each round of the tournament. Players are paired or automatically advance in each round. By simulating the rounds, we determine when the firstPlayer and secondPlayer can potentially meet by adjusting the match outcomes strategically.

import math

def earliest_and_latest_rounds(n, firstPlayer, secondPlayer):
    earliestRound = 0
    latestRound = 0
    minRound = 1
    maxRound = int(math.log2(n)) + 1
    for round in range(minRound, maxRound + 1):
        matchPos = 1
        left, right = 1, n
        while left <= right:
            if (left == firstPlayer and right == secondPlayer) or (left == secondPlayer and right == firstPlayer):
                if earliestRound == 0:
                    earliestRound = round
                latestRound = round
            if left == right:
                break
            left += 1
            right -= 1
            matchPos += 1
    return [earliestRound, latestRound]

result = earliest_and_latest_rounds(11, 2, 4)
print(f"Earliest Round: {result[0]}, Latest Round: {result[1]}")
import java.util.*;

public class Tournament {
    public static int[] earliestAndLatestRounds(int n, int firstPlayer, int secondPlayer) {
        int[] result = new int[2];
        int earliestRound = 0, latestRound = 0;
        int minRound = 1;
        int maxRound = (int) Math.log(n) / Math.log(2) + 1;
        for (int round = minRound; round <= maxRound; ++round) {
            int matchPos = 1;
            for (int left = 1, right = n; left <= right; ) {
                if ((left == firstPlayer && right == secondPlayer) || (left == secondPlayer && right == firstPlayer)) {
                    if (earliestRound == 0) earliestRound = round;
                    latestRound = round;
                }
                if (left == right) break;
                left++; right--;
                matchPos++;
            }
        }
        result[0] = earliestRound;
        result[1] = latestRound;
        return result;
    }
    public static void main(String[] args) {
        int[] result = earliestAndLatestRounds(11, 2, 4);
        System.out.println("Earliest Round: " + result[0] + ", Latest Round: " + result[1]);
    }
}
#include <iostream>
#include <cmath>
using namespace std;

pair<int, int> earliestAndLatestRounds(int n, int firstPlayer, int secondPlayer) {
    int earliestRound = 0, latestRound = 0;
    int minRound = 1;
    int maxRound = (int) log2(n) + 1;
    for (int round = minRound; round <= maxRound; ++round) {
        int matchPos = 1;
        for (int left = 1, right = n; left <= right; ) {
            if ((left == firstPlayer && right == secondPlayer) || (left == secondPlayer && right == firstPlayer)) {
                if (earliestRound == 0) earliestRound = round;
                latestRound = round;
            }
            if (left == right) break;
            left++; right--;
            matchPos++;
        }
    }
    return make_pair(earliestRound, latestRound);
}

int main() {
    auto result = earliestAndLatestRounds(11, 2, 4);
    cout << "Earliest Round: " << result.first << ", Latest Round: " << result.second << endl;
    return 0;
}
function earliestAndLatestRounds(n, firstPlayer, secondPlayer) {
    let earliestRound = 0, latestRound = 0;
    const minRound = 1;
    const maxRound = Math.floor(Math.log2(n)) + 1;
    for (let round = minRound; round <= maxRound; ++round) {
        let matchPos = 1;
        let left = 1, right = n;
        while (left <= right) {
            if ((left === firstPlayer && right === secondPlayer) || (left === secondPlayer && right === firstPlayer)) {
                if (earliestRound === 0) earliestRound = round;
                latestRound = round;
            }
            if (left === right) break;
            left++; right--;
            matchPos++;
        }
    }
    return [earliestRound, latestRound];
}

const result = earliestAndLatestRounds(11, 2, 4);
console.log(`Earliest Round: ${result[0]}, Latest Round: ${result[1]}`);
#include <stdio.h>
#include <math.h>

int* earliestAndLatestRounds(int n, int firstPlayer, int secondPlayer, int* returnSize) {
    int earliestRound = 0, latestRound = 0;
    int minRound = 1;
    int maxRound = (int) log2(n) + 1;
    for (int round = minRound; round <= maxRound; ++round) {
        int matchPos = 1;
        for (int left = 1, right = n; left <= right;)
        {
            if ((left == firstPlayer && right == secondPlayer) || (left == secondPlayer && right == firstPlayer)) {
                if (earliestRound == 0) earliestRound = round;
                latestRound = round;
            }
            if (left == right) break;
            left++; right--;
            matchPos++;
        }
    }
    *returnSize = 2;
    int *res = (int *) malloc(sizeof(int) * 2);
    res[0] = earliestRound;
    res[1] = latestRound;
    return res;
}

int main() {
    int returnSize;
    int* result = earliestAndLatestRounds(11, 2, 4, &returnSize);
    printf("Earliest Round: %d, Latest Round: %d\n", result[0], result[1]);
    free(result);
    return 0;
}
using System;

public class Tournament {
    public static int[] EarliestAndLatestRounds(int n, int firstPlayer, int secondPlayer) {
        int earliestRound = 0, latestRound = 0;
        int minRound = 1;
        int maxRound = (int) Math.Log(n, 2) + 1;
        for (int round = minRound; round <= maxRound; ++round) {
            int matchPos = 1;
            for (int left = 1, right = n; left <= right; ) {
                if ((left == firstPlayer && right == secondPlayer) || (left == secondPlayer && right == firstPlayer)) {
                    if (earliestRound == 0) earliestRound = round;
                    latestRound = round;
                }
                if (left == right) break;
                left++; right--;
                matchPos++;
            }
        }
        return new int[] { earliestRound, latestRound };
    }

    public static void Main() {
        int[] result = EarliestAndLatestRounds(11, 2, 4);
        Console.WriteLine("Earliest Round: " + result[0] + ", Latest Round: " + result[1]);
    }
}

The Python function implements logic similar to C/C++ methods, calculating and verifying possible matchups between firstPlayer and secondPlayer through tournament rounds.

Complexity

Time Complexity: O(log n), Space Complexity: O(1)

Try this approach in the editor

Approach 2: Recursive Approach

This approach leverages a recursive function to simulate each possible matchup and determine when firstPlayer and secondPlayer may potentially meet. Using recursion, we can perform this task dynamically, exploring all feasible scenarios and calculating the earliest and latest rounds on the way.

def dfs(n, f, s, round, res):
    if n == 1:
        return
    if f > s:
        f, s = s, f
    mid = (n + 1) // 2
    if (f <= mid < s) or n == 2:
        if res[0] == 0:
            res[0] = round
        res[1] = round
        return
    dfs(mid, f, s, round + 1, res)
    dfs(mid, f if f <= mid else f - mid, s if s <= mid else s - mid, round + 1, res)

def earliest_and_latest_rounds(n, firstPlayer, secondPlayer):
    res = [0, 0]
    dfs(n, firstPlayer, secondPlayer, 1, res)
    return res

result = earliest_and_latest_rounds(11, 2, 4)
print(f"Earliest Round: {result[0]}, Latest Round: {result[1]}")
import java.util.*;

public class Tournament {
    private static void dfs(int n, int f, int s, int round, int[] res) {
        if (n == 1) return;
        if (f > s) { int temp = f; f = s; s = temp; }

        int mid = (n + 1) / 2;
        if ((f <= mid && s > mid) || n == 2) {
            if (res[0] == 0) res[0] = round;
            res[1] = round;
            return;
        }

        dfs(mid, f, s, round + 1, res);
        dfs(mid, f <= mid ? f : f - mid, s <= mid ? s : s - mid, round + 1, res);
    }

    public static int[] earliestAndLatestRounds(int n, int firstPlayer, int secondPlayer) {
        int[] result = new int[2];
        dfs(n, firstPlayer, secondPlayer, 1, result);
        return result;
    }

    public static void main(String[] args) {
        int[] result = earliestAndLatestRounds(11, 2, 4);
        System.out.println("Earliest Round: " + result[0] + ", Latest Round: " + result[1]);
    }
}
#include <iostream>
#include <vector>
using namespace std;

void dfs(int n, int f, int s, int round, int &earliest, int &latest) {
    if (n == 1) return;
    if (f > s) swap(f, s);

    int mid = (n + 1) / 2;
    if ((f <= mid && s > mid) || n == 2) {
        if (earliest == 0) earliest = round;
        latest = round;
        return;
    }

    dfs(mid, f, s, round + 1, earliest, latest);
    dfs(mid, f <= mid ? f : f - mid, s <= mid ? s : s - mid, round + 1, earliest, latest);
}

vector<int> earliestAndLatestRounds(int n, int firstPlayer, int secondPlayer) {
    int earliest = 0, latest = 0;
    dfs(n, firstPlayer, secondPlayer, 1, earliest, latest);
    return {earliest, latest};
}

int main() {
    vector<int> result = earliestAndLatestRounds(11, 2, 4);
    cout << "Earliest Round: " << result[0] << ", Latest Round: " << result[1] << endl;
    return 0;
}
function dfs(n, f, s, round, res) {
    if (n === 1) return;
    if (f > s) [f, s] = [s, f];

    const mid = Math.ceil((n + 1) / 2);
    if ((f <= mid && s > mid) || n === 2) {
        if (res[0] === 0) res[0] = round;
        res[1] = round;
        return;
    }

    dfs(mid, f, s, round + 1, res);
    dfs(mid, f <= mid ? f : f - mid, s <= mid ? s : s - mid, round + 1, res);
}

function earliestAndLatestRounds(n, firstPlayer, secondPlayer) {
    const result = [0, 0];
    dfs(n, firstPlayer, secondPlayer, 1, result);
    return result;
}

const result = earliestAndLatestRounds(11, 2, 4);
console.log(`Earliest Round: ${result[0]}, Latest Round: ${result[1]}`);
#include <stdio.h>
#include <stdlib.h>

void dfs(int n, int f, int s, int round, int *earliest, int *latest) {
    if (n == 1) return;
    if (f > s) { int temp = f; f = s; s = temp; } 

    int mid = (n + 1) / 2;
    if ((f <= mid && s > mid) || n == 2) {
        if (*earliest == 0) *earliest = round;
        *latest = round;
        return;
    }

    dfs(mid, f, s, round + 1, earliest, latest);
    dfs(mid, f <= mid ? f : f - mid, s <= mid ? s : s - mid, round + 1, earliest, latest);
}

int* earliestAndLatestRounds(int n, int firstPlayer, int secondPlayer, int* returnSize) {
    int *result = (int *)malloc(sizeof(int) * 2);
    int earliest = 0, latest = 0;
    dfs(n, firstPlayer, secondPlayer, 1, &earliest, &latest);
    result[0] = earliest;
    result[1] = latest;
    *returnSize = 2;
    return result;
}

int main() {
    int returnSize;
    int* result = earliestAndLatestRounds(11, 2, 4, &returnSize);
    printf("Earliest Round: %d, Latest Round: %d\n", result[0], result[1]);
    free(result);
    return 0;
}
using System;

public class Tournament {
    private static void dfs(int n, int f, int s, int round, int[] res) {
        if (n == 1) return;
        if (f > s) { int temp = f; f = s; s = temp; }

        int mid = (n + 1) / 2;
        if (f <= mid && s > mid || n == 2) {
            if (res[0] == 0) res[0] = round;
            res[1] = round;
            return;
        }

        dfs(mid, f, s, round + 1, res);
        dfs(mid, f <= mid ? f : f - mid, s <= mid ? s : s - mid, round + 1, res);
    }

    public static int[] EarliestAndLatestRounds(int n, int firstPlayer, int secondPlayer) {
        var result = new int[2];
        dfs(n, firstPlayer, secondPlayer, 1, result);
        return result;
    }

    public static void Main() {
        var result = EarliestAndLatestRounds(11, 2, 4);
        Console.WriteLine("Earliest Round: " + result[0] + ", Latest Round: " + result[1]);
    }
}

The Python code uses DFS recursion to assess different tournament trees, calculating the rounds when player clashes become possible.

Complexity

Time Complexity: Exponential, Space Complexity: O(log n)

Try this approach in the editor

Approach 3: Memoization + Binary Enumeration

We define a function dfs(l, r, n), which represents the earliest and latest rounds where players numbered l and r compete among n players in the current round.

The execution logic of function dfs(l, r, n) is as follows:

  1. If l + r = n - 1, it means the two players compete in the current round, return [1, 1].
  2. If f[l][r][n] neq 0, it means this state has been calculated before, directly return the result.
  3. Initialize the earliest round number as positive infinity and the latest round number as negative infinity.
  4. Calculate the number of players in the first half of the current round m = n / 2.
  5. Enumerate all possible winner combinations of the first half (using binary enumeration), for each combination:
    • Determine which players win based on the current combination.
    • Determine the positions of players numbered l and r in the current round.
    • Count the positions of players numbered l and r among the remaining players, denoted as a and b, and the total number of remaining players c.
    • Recursively call dfs(a, b, c) to get the earliest and latest round numbers for the current state.
    • Update the earliest and latest round numbers.
  6. Store the calculation result in f[l][r][n] and return the earliest and latest round numbers.

The answer is dfs(firstPlayer - 1, secondPlayer - 1, n).

function earliestAndLatest(n: number, firstPlayer: number, secondPlayer: number): number[] {
    return dfs(firstPlayer - 1, secondPlayer - 1, n);
}

const f: number[][][] = Array.from({ length: 30 }, () =>
    Array.from({ length: 30 }, () => Array(31).fill(0)),
);

function dfs(l: number, r: number, n: number): number[] {
    if (f[l][r][n] !== 0) {
        return decode(f[l][r][n]);
    }
    if (l + r === n - 1) {
        f[l][r][n] = encode(1, 1);
        return [1, 1];
    }

    let min = Number.MAX_SAFE_INTEGER;
    let max = Number.MIN_SAFE_INTEGER;
    const m = n >> 1;

    for (let i = 0; i < 1 << m; i++) {
        const win: boolean[] = Array(n).fill(false);
        for (let j = 0; j < m; j++) {
            if ((i >> j) & 1) {
                win[j] = true;
            } else {
                win[n - 1 - j] = true;
            }
        }

        if (n & 1) {
            win[m] = true;
        }

        win[n - 1 - l] = false;
        win[n - 1 - r] = false;
        win[l] = true;
        win[r] = true;

        let a = 0,
            b = 0,
            c = 0;
        for (let j = 0; j < n; j++) {
            if (j === l) a = c;
            if (j === r) b = c;
            if (win[j]) c++;
        }

        const t = dfs(a, b, c);
        min = Math.min(min, t[0] + 1);
        max = Math.max(max, t[1] + 1);
    }

    f[l][r][n] = encode(min, max);
    return [min, max];
}

function encode(x: number, y: number): number {
    return (x << 8) | y;
}

function decode(val: number): number[] {
    return [val >> 8, val & 255];
}
var f [30][30][31]int

func earliestAndLatest(n int, firstPlayer int, secondPlayer int) []int {
	return dfs(firstPlayer-1, secondPlayer-1, n)
}

func dfs(l, r, n int) []int {
	if f[l][r][n] != 0 {
		return decode(f[l][r][n])
	}
	if l+r == n-1 {
		f[l][r][n] = encode(1, 1)
		return []int{1, 1}
	}

	min, max := 1<<30, -1<<31
	m := n >> 1

	for i := 0; i < (1 << m); i++ {
		win := make([]bool, n)
		for j := 0; j < m; j++ {
			if (i>>j)&1 == 1 {
				win[j] = true
			} else {
				win[n-1-j] = true
			}
		}
		if n&1 == 1 {
			win[m] = true
		}
		win[n-1-l] = false
		win[n-1-r] = false
		win[l] = true
		win[r] = true

		a, b, c := 0, 0, 0
		for j := 0; j < n; j++ {
			if j == l {
				a = c
			}
			if j == r {
				b = c
			}
			if win[j] {
				c++
			}
		}

		t := dfs(a, b, c)
		if t[0]+1 < min {
			min = t[0] + 1
		}
		if t[1]+1 > max {
			max = t[1] + 1
		}
	}

	f[l][r][n] = encode(min, max)
	return []int{min, max}
}

func encode(x, y int) int {
	return (x << 8) | y
}

func decode(val int) []int {
	return []int{val >> 8, val & 255}
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Simulation Approach

Time Complexity: O(log n), Space Complexity: O(1)

Recursive Approach

Time Complexity: Exponential, Space Complexity: O(log n)

Memoization + Binary Enumeration–

Complexity analysis

ApproachTimeSpaceWhen to Use
Simulation ApproachExponentialO(states)Useful for understanding tournament mechanics or small n where exploring all match outcomes is feasible.
Recursive DP with MemoizationO(n^3)O(n^3)General optimal solution. Efficiently computes earliest and latest meeting rounds by caching repeated bracket states.

Frequently asked questions

Is The Earliest and Latest Rounds Where Players Compete easy or hard?

The problem is classified as Hard because it requires modeling tournament rounds and reducing the state space using dynamic programming. Recognizing that only player positions matter, not full bracket permutations, is the key insight needed for an efficient solution.

The Earliest and Latest Rounds Where Players Compete Python/Java solution

Python and Java implementations usually use recursion with memoization. Python often uses functools.lru_cache to store DP states, while Java uses a HashMap or multidimensional array. Both versions follow the same state transition logic and achieve O(n^3) complexity.

How to solve The Earliest and Latest Rounds Where Players Compete in O(n^3)?

Use recursion with memoization where the state is (n, firstPlayer, secondPlayer). For each round, compute all possible ways other matches can resolve, determining how many winners appear before each tracked player in the next bracket. Recursively evaluate these states and cache results to avoid recomputation, giving an O(n^3) dynamic programming solution.

What is the best approach for The Earliest and Latest Rounds Where Players Compete?

The best approach uses recursive dynamic programming with memoization. The state tracks the number of players and the current positions of the two tracked competitors. By exploring valid winner combinations for each round and caching results, the algorithm avoids recomputation and runs in about O(n^3) time with O(n^3) space.

Is The Earliest and Latest Rounds Where Players Compete asked at Google/Amazon/Meta?

Tournament bracket and dynamic programming problems similar to this one appear in interviews at companies like Google, Amazon, and Meta. The question tests recursive state modeling, memoization, and reasoning about combinational outcomes in constrained simulations.

What data structure is used in The Earliest and Latest Rounds Where Players Compete?

The core data structure is a memoization cache, typically implemented with a hash map or a 3D DP array keyed by (n, firstPlayer, secondPlayer). Recursion explores state transitions while the cache stores previously computed earliest and latest rounds.

What is the time complexity of The Earliest and Latest Rounds Where Players Compete?

The optimized dynamic programming solution runs in O(n^3) time and O(n^3) space. The DP state is defined by the number of players and the relative positions of the two tracked players, and each state explores possible distributions of winners before the next round.

Comments

Now write it yourselfClose this page and solve The Earliest and Latest Rounds Where Players Compete in the editor, without looking.
Practise The Earliest and Latest Rounds Where Players Compete