Skip to content
← All posts

Card Flipping Game: Finding the Minimum Good Number

5 min read
leetcodeproblemmediumarrayshash-map

LeetCode 822. Card Flipping Game gives you a row of cards lying on a table. Each card has a number on its front and a number on its back. You can flip any subset of cards. After flipping, you want to find the minimum number x such that x appears on the front of at least one card, and no card showing x on its front also has x on its back.

If no such number exists, return 0.

The wording of this problem is notoriously confusing. Let's cut through the noise and look at what actually matters.

front1back1card 0same!front2back3card 1front4back2card 2front4back1card 3front7back3card 4Eliminated (front = back):{1}Valid candidates:[2, 3, 4, 7]min = 2
Card 0 has front = back = 1, so 1 is eliminated. The remaining numbers (2, 3, 4, 7) are all valid. The minimum is 2.

The key insight

Here is the observation that unlocks the entire problem: if a card has the same number on both its front and its back, that number can never be "good." Why? Because no matter which way you flip that card, that number is visible. You cannot hide it. And since it is always showing on the front of that card, any other card showing the same number on its front will be blocked by this card.

So the algorithm is surprisingly clean:

  1. Find every number that appears on both sides of the same card. Collect these into a "same" set.
  2. Scan through all numbers in fronts and backs. Any number not in the same set is a valid candidate.
  3. Return the minimum candidate. If there are no candidates, return 0.

That is the entire solution. No actual flipping simulation needed.

The solution

def flipgame(fronts: list[int], backs: list[int]) -> int:
    same = set()
    for f, b in zip(fronts, backs):
        if f == b:
            same.add(f)

    result = float("inf")
    for f, b in zip(fronts, backs):
        if f not in same:
            result = min(result, f)
        if b not in same:
            result = min(result, b)

    return result if result < float("inf") else 0

Two passes through the arrays. The first pass builds the set of disqualified numbers. The second pass finds the minimum number that is not disqualified. Clean and efficient.

Step-by-step walkthrough

Let's trace through the example: fronts = [1, 2, 4, 4, 7], backs = [1, 3, 2, 1, 3].

Step 1: Identify same-side cards

fronts = [1, 2, 4, 4, 7], backs = [1, 3, 2, 1, 3] => Card 0: front=1, back=1 (same!)

Scan all cards. If fronts[i] == backs[i], that number can never be hidden by flipping. Add it to the 'same' set.

Step 2: Build the same set

same = {1}

Only card 0 has the same value on both sides. The number 1 is permanently disqualified.

Step 3: Search for the minimum valid number

All values: [1, 2, 4, 4, 7, 1, 3, 2, 1, 3] => skip any in same set

Scan through every number in fronts and backs. If a number is not in the same set, it is a candidate. Track the smallest one.

Step 4: Evaluate each candidate

1 => in same, skip | 2 => not in same, candidate (min so far = 2) | 3 => not in same | 4 => not in same | 7 => not in same

The number 2 appears on card 1 (front) and card 2 (back). Since neither card has front = back = 2, we can always flip to show 2 on the front of one card and hide it elsewhere.

Step 5: Return the answer

return 2

The minimum number that is not in the same set is 2. This is the smallest 'good' number we can guarantee on the front of at least one card.

Why does this work?

Think about it from the perspective of a single number, say 2. The number 2 appears on the front of card 1 and on the back of card 2. Is there any arrangement of flips where 2 is on the front of some card, and no card showing 2 on its front has 2 on its back?

Yes. Leave card 1 as-is (front = 2, back = 3). Flip card 2 so its front becomes 2 and its back becomes 4. Now card 1 shows front = 2 with back = 3 (not 2), and card 2 shows front = 2 with back = 4 (not 2). The number 2 works.

The only situation where a number cannot work is when some card has that number on both sides. That card will always show the number on its front, and it will always have the same number on its back. No flip can fix that.

Complexity analysis

MetricValue
TimeO(n)
SpaceO(n)

You iterate through the arrays twice (once to build the set, once to find the minimum). The set stores at most n values. Everything runs in linear time.

Building blocks

This problem is built on two fundamental patterns that appear across many LeetCode problems.

1. Elimination set

The idea of collecting values that are disqualified and then filtering against them. You build a set of "bad" values in one pass, then use O(1) lookups to skip them in a second pass. This same technique shows up in problems like Contains Duplicate (where the set tracks "seen" values) and in frequency-based problems where you need to exclude certain elements.

bad = set()
for item in collection:
    if should_eliminate(item):
        bad.add(item)

2. Minimum over filtered candidates

After eliminating bad values, you scan for the minimum among what remains. This is a standard reduce operation that pairs naturally with the elimination set. You will see this "filter then minimize" pattern in problems involving constraints, thresholds, or conditional selection.

result = float("inf")
for candidate in all_values:
    if candidate not in bad:
        result = min(result, candidate)

Edge cases

  • All cards have the same front and back: Every number is in the same set, so no valid candidate exists. Return 0.

  • No cards have matching front and back: Every number in both arrays is a valid candidate. Return the overall minimum across both arrays.

  • Single card with different front and back: Both the front and back values are candidates. Return the smaller of the two.

  • Large duplicate values: Multiple cards can share the same number. As long as that number does not appear on both sides of any single card, it remains a valid candidate regardless of how many times it appears elsewhere.

From understanding to recall

Reading this solution takes a few minutes. Reproducing it under interview pressure, two weeks from now, is a different challenge. The core of this problem is recognizing that "same front and back" means permanent disqualification, then applying a set-based filter. That recognition needs to be automatic, not something you rediscover from scratch.

Spaced repetition helps. CodeBricks breaks this problem into its two building blocks (the elimination set and the filtered minimum scan) and drills them at increasing intervals. You type each piece from memory, not by copying, until the pattern is second nature.

Related posts

  • Contains Duplicate - The simplest set-based problem, where you track seen values and check membership in O(1)
  • Hash Map Patterns - A deeper look at how hash sets and hash maps power dozens of LeetCode solutions
  • Find the Duplicate Number - Another problem about finding duplicates, this time with tighter space constraints and cycle detection

Once you see that Card Flipping Game is really just "build a set of bad numbers, then find the minimum good one," the problem stops being confusing. The tricky part was never the code. It was understanding which numbers are permanently disqualified. With that insight locked in, the implementation writes itself.