The Elimination Game problem is as follows: Given an integer n
, imagine a list of numbers from 1
to n
in increasing order. You perform the following operation until only one number remains:
Key constraints:
n
.1 <= n <= 10^9
At first glance, it may seem that we need to simulate the entire elimination process, removing elements from an array until one remains. However, with large values of n
(up to 109), this approach would be extremely inefficient and use too much memory.
The key insight is to recognize patterns in the elimination process. After each pass, half of the numbers are removed, and the remaining numbers form a new sequence with a different starting point and step size. By tracking how the head of the list, the step size, and the direction change after each elimination, we can avoid simulating the entire array and instead use a mathematical approach to find the answer efficiently.
We can model the elimination process by maintaining four variables:
head = 1
, step = 1
, remaining = n
, and left = True
(start from the left).remaining > 1
:
remaining
is odd, update head = head + step
(because the first number is always eliminated in these cases).step = step * 2
(since every other number is removed, the gap doubles).remaining = remaining // 2
(half the numbers remain).left
(switch direction).head
as the last remaining number.This approach avoids array simulation and works in logarithmic time.
Let's walk through the process for n = 9
:
Initial list: 1 2 3 4 5 6 7 8 9
O(n)
time and O(n)
space, which is infeasible for large n
.
n
to 1, which is O(\log n)
time and O(1)
space. This is because after each elimination round, the number of remaining elements halves.
The Elimination Game problem is elegantly solved by recognizing the pattern in the elimination process and tracking only the head, step, and direction. This allows us to avoid simulating the entire list and instead use a mathematical approach that is both fast and memory-efficient, making it suitable even for very large values of n
.
class Solution:
def lastRemaining(self, n: int) -> int:
head = 1
step = 1
remaining = n
left = True
while remaining > 1:
if left or remaining % 2 == 1:
head += step
step *= 2
remaining //= 2
left = not left
return head
class Solution {
public:
int lastRemaining(int n) {
int head = 1;
int step = 1;
int remaining = n;
bool left = true;
while (remaining > 1) {
if (left || remaining % 2 == 1) {
head += step;
}
step *= 2;
remaining /= 2;
left = !left;
}
return head;
}
};
class Solution {
public int lastRemaining(int n) {
int head = 1;
int step = 1;
int remaining = n;
boolean left = true;
while (remaining > 1) {
if (left || remaining % 2 == 1) {
head += step;
}
step *= 2;
remaining /= 2;
left = !left;
}
return head;
}
}
var lastRemaining = function(n) {
let head = 1;
let step = 1;
let remaining = n;
let left = true;
while (remaining > 1) {
if (left || remaining % 2 === 1) {
head += step;
}
step *= 2;
remaining = Math.floor(remaining / 2);
left = !left;
}
return head;
};