152 lines
7.9 KiB
YAML
152 lines
7.9 KiB
YAML
title: Chalkboard XOR Game
|
|
slug: chalkboard-xor-game
|
|
difficulty: hard
|
|
leetcode_id: 810
|
|
leetcode_url: https://leetcode.com/problems/chalkboard-xor-game/
|
|
categories:
|
|
- arrays
|
|
- math
|
|
patterns:
|
|
- greedy
|
|
|
|
description: |
|
|
You are given an array of integers `nums` representing the numbers written on a chalkboard.
|
|
|
|
Alice and Bob take turns erasing exactly one number from the chalkboard, with Alice starting first. If erasing a number causes the bitwise XOR of all the elements of the chalkboard to become `0`, then that player loses. The bitwise XOR of one element is that element itself, and the bitwise XOR of no elements is `0`.
|
|
|
|
Also, if any player starts their turn with the bitwise XOR of all the elements of the chalkboard equal to `0`, then that player wins.
|
|
|
|
Return `true` *if and only if Alice wins the game, assuming both players play optimally*.
|
|
|
|
constraints: |
|
|
- `1 <= nums.length <= 1000`
|
|
- `0 <= nums[i] < 2^16`
|
|
|
|
examples:
|
|
- input: "nums = [1,1,2]"
|
|
output: "false"
|
|
explanation: "Alice has two choices: erase 1 or erase 2. If she erases 1, nums becomes [1, 2] with XOR = 3. Bob can then force Alice to erase the last element and lose. If Alice erases 2, nums becomes [1, 1] with XOR = 0, so Alice loses immediately."
|
|
- input: "nums = [0,1]"
|
|
output: "true"
|
|
explanation: "Alice can erase 1, leaving [0]. The XOR is 0, but it was already non-zero before her move completed, and now Bob faces XOR = 0 at the start of his turn, so Bob loses."
|
|
- input: "nums = [1,2,3]"
|
|
output: "true"
|
|
explanation: "XOR of [1,2,3] = 0. Alice starts with XOR = 0, so she wins immediately."
|
|
|
|
explanation:
|
|
intuition: |
|
|
This problem looks like it requires game simulation or minimax, but there's a beautiful mathematical insight that reduces it to a simple formula.
|
|
|
|
Think about XOR properties: if the XOR of all elements is already `0` when it's your turn, you win. The key insight is understanding *when* Alice can be forced into a losing position.
|
|
|
|
Alice loses only if **every** number she could erase would make the XOR become `0`. Let's think about when this happens. If the current XOR is `X ≠ 0`, and Alice must pick a number `nums[i]` such that `X XOR nums[i] = 0`, then `nums[i] = X`. This means Alice loses only if **all** remaining numbers equal the current XOR value — which means all numbers are identical.
|
|
|
|
But here's the clever part: if all `n` numbers are identical (say, all equal to `v`), then:
|
|
- If `n` is even: XOR = `v XOR v XOR ... = 0`, so Alice wins immediately
|
|
- If `n` is odd: XOR = `v`, and Alice must erase `v`, leaving an even count where Bob faces XOR = 0 and wins
|
|
|
|
Generalising this: Alice loses only when forced to make XOR = 0. With an even number of elements and non-zero XOR, Alice can always find a "safe" move (at least one element whose removal doesn't make XOR = 0). This is because if removing **any** element made XOR = 0, all elements would need to equal the current XOR, making them all identical — but then with even count, XOR would already be 0.
|
|
|
|
Therefore: **Alice wins if and only if the initial XOR is 0, OR the array has an even length.**
|
|
|
|
approach: |
|
|
The solution is remarkably simple once you understand the game theory:
|
|
|
|
**Step 1: Calculate the XOR of all elements**
|
|
|
|
- Compute `xor_sum` by XORing all elements in `nums`
|
|
- This tells us the starting game state
|
|
|
|
|
|
|
|
**Step 2: Check the two winning conditions for Alice**
|
|
|
|
- **Condition 1:** If `xor_sum == 0`, Alice wins immediately (she starts with XOR = 0)
|
|
- **Condition 2:** If `len(nums)` is even, Alice wins (she can always find a safe move)
|
|
|
|
|
|
|
|
**Step 3: Return the result**
|
|
|
|
- Return `True` if either condition is met, `False` otherwise
|
|
- This covers all cases: Alice wins if she starts with XOR = 0, or if she has an even number of elements to work with
|
|
|
|
|
|
|
|
The mathematical proof: with even length and non-zero XOR, if removing any element made XOR = 0, then each element `nums[i]` would equal the total XOR. But `n` identical values XORed together (with `n` even) gives 0, contradicting our assumption that XOR ≠ 0.
|
|
|
|
common_pitfalls:
|
|
- title: Attempting Game Simulation
|
|
description: |
|
|
A natural first instinct is to simulate the game using recursion or dynamic programming, trying all possible moves for both players.
|
|
|
|
With up to 1000 elements, this approach has exponential complexity — the game tree branches at each turn, leading to `O(n!)` possibilities. This will cause **Time Limit Exceeded**.
|
|
|
|
The key is recognising this as a **mathematical problem** with a closed-form solution, not a search problem.
|
|
wrong_approach: "Recursive simulation with memoisation"
|
|
correct_approach: "Use XOR properties and parity analysis"
|
|
|
|
- title: Forgetting the Even-Length Guarantee
|
|
description: |
|
|
Some solutions correctly check if XOR = 0 but miss the even-length winning condition.
|
|
|
|
The insight is subtle: with an even number of elements and non-zero XOR, Alice can *always* find at least one element whose removal doesn't make XOR = 0. This is guaranteed by the pigeonhole principle applied to XOR values.
|
|
wrong_approach: "Only checking if initial XOR is 0"
|
|
correct_approach: "Check both XOR = 0 and even length conditions"
|
|
|
|
- title: Misunderstanding the Winning Condition
|
|
description: |
|
|
The rules have a subtle distinction:
|
|
- If you **start** your turn with XOR = 0, you **win**
|
|
- If your move **causes** XOR to become 0, you **lose**
|
|
|
|
These are different! Starting with XOR = 0 is an immediate win, not a loss. This catches players who think XOR = 0 is always bad.
|
|
wrong_approach: "Thinking XOR = 0 means current player loses"
|
|
correct_approach: "XOR = 0 at turn start means win; causing XOR = 0 means loss"
|
|
|
|
key_takeaways:
|
|
- "**Game theory simplification**: Many two-player games have elegant mathematical solutions — look for invariants and parity arguments before attempting simulation"
|
|
- "**XOR properties**: XOR of identical values cancels out (even count = 0), which drives the parity-based solution"
|
|
- "**Constraint analysis**: The even-length guarantee comes from a proof by contradiction using XOR algebra"
|
|
- "**Pattern recognition**: This problem teaches that 'hard' brainteasers often have O(n) or O(1) solutions hiding behind the complexity"
|
|
|
|
time_complexity: "O(n). We traverse the array once to compute the XOR of all elements."
|
|
space_complexity: "O(1). We only use a single variable (`xor_sum`) regardless of input size."
|
|
|
|
solutions:
|
|
- approach_name: XOR and Parity Check
|
|
is_optimal: true
|
|
code: |
|
|
def xor_game(nums: list[int]) -> bool:
|
|
# Calculate XOR of all elements
|
|
xor_sum = 0
|
|
for num in nums:
|
|
xor_sum ^= num
|
|
|
|
# Alice wins if:
|
|
# 1. XOR is already 0 (she wins immediately), OR
|
|
# 2. Array has even length (she can always find a safe move)
|
|
return xor_sum == 0 or len(nums) % 2 == 0
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass to compute XOR.
|
|
|
|
**Space Complexity:** O(1) — Only one integer variable used.
|
|
|
|
The solution uses game theory insight: Alice wins if she starts with XOR = 0, or if she has an even number of elements (guaranteeing a safe move exists). This elegant formula avoids expensive game tree simulation.
|
|
|
|
- approach_name: One-Liner with Reduce
|
|
is_optimal: true
|
|
code: |
|
|
from functools import reduce
|
|
from operator import xor
|
|
|
|
def xor_game(nums: list[int]) -> bool:
|
|
# XOR all elements; Alice wins if result is 0 or length is even
|
|
return reduce(xor, nums) == 0 or len(nums) % 2 == 0
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Reduce iterates through all elements.
|
|
|
|
**Space Complexity:** O(1) — No additional space beyond the accumulator.
|
|
|
|
A more Pythonic version using `functools.reduce` with the XOR operator. Functionally identical to the explicit loop but more concise.
|