Bird
Raised Fist0
Interview Prepdp-advanced-trees-bitmaskhardGoogle

Count Ways to Assign Unique Cap

Choose your preparation mode4 modes available
▶
Steps
setup

Initialize DP array

We create a dp array of size 8 (2^3) initialized to zero, then set dp[0] = 1 to represent one way to assign no caps.

💡 Starting with dp[0] = 1 means there is exactly one way to assign caps to no one - the empty assignment.
Line:dp = [0] * (1 << n) dp[0] = 1
💡 This initialization sets the base case for building up assignments incrementally.
📊
Count Ways to Assign Unique Cap - Watch the Algorithm Execute, Step by Step
Watching this step-by-step shows how bitmask DP efficiently counts assignments without enumerating all permutations explicitly.
Step 1/10
·Active fill★Answer cell
Item 0 - wt:0 val:0
i\w01234567
i=010000000
dp[0] = 1 (base case)
Item 0 - wt:0 val:0
i\w01234567
i=010000000
cap_to_persons mapping ready
Item 0 - wt:1 val:0
i\w01234567
i=010101000
dp[0] used to assign cap 1
Item 0 - wt:2 val:0
i\w01234567
i=010102000
dp[0] used to assign cap 2
Item 0 - wt:5 val:0
i\w01234567
i=011212000
dp[0] used to assign cap 5
Item 0 - wt:100 val:0
i\w01234567
i=012232114
dp[0] used to assign cap 100
Item 0 - wt:0 val:0
i\w01234567
i=012232114
Skipping irrelevant caps
Item 0 - wt:0 val:0
i\w01234567
i=012232114
Answer: dp[7] = 4
Item 0 - wt:0 val:4
i\w01234567
i=012232114
Final answer cell
Item 0 - wt:0 val:0
i\w01234567
i=012232114
dp array overview

Key Takeaways

✓ Bitmask DP efficiently counts unique assignments by representing subsets of assigned persons as bitmasks.

This insight is hard to see from code alone because the bitmask representation and backward iteration are subtle but crucial.

✓ Backward iteration over dp masks prevents double counting when assigning the same cap to multiple persons.

Seeing the order of iteration visually clarifies why forward iteration would produce incorrect counts.

✓ Preprocessing caps to persons allows skipping irrelevant caps, improving efficiency without affecting correctness.

The trace shows how skipping empty caps focuses computation only where needed.

Practice

(1/5)
1. Consider the following Python code implementing the optimized LIS algorithm. What is the value of the variable tails after processing the input [3, 1, 2]?
easy
A. [1, 2, 3]
B. [1, 2]
C. [1, 3]
D. [3, 1, 2]

Solution

  1. Step 1: Process num=3

    tails is empty, bisect_left returns 0, append 3 -> tails = [3]
  2. Step 2: Process num=1

    bisect_left(tails,1) returns 0, replace tails[0] with 1 -> tails = [1]
  3. Step 3: Process num=2

    bisect_left(tails,2) returns 1 (end of tails), append 2 -> tails = [1, 2]
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    tails always sorted and minimal last elements [OK]
Hint: tails stores smallest possible ends of subsequences [OK]
Common Mistakes:
  • Appending instead of replacing in tails
  • Misunderstanding bisect_left index
  • Confusing tails with actual subsequence
2. You are given an integer array and need to find how many distinct longest strictly increasing subsequences it contains. Which approach guarantees an optimal solution that efficiently counts all such subsequences without enumerating them explicitly?
easy
A. A greedy algorithm that picks the first increasing subsequence found and counts its length
B. A pure recursive backtracking approach that tries all subsequences to count the longest ones
C. Sorting the array and counting increasing subsequences using combinatorial formulas
D. Dynamic programming that tracks length and count of LIS ending at each index with O(n²) time complexity

Solution

  1. Step 1: Understand problem requirements

    The problem requires counting all longest increasing subsequences, not just finding one or their length.
  2. Step 2: Evaluate approaches

    Greedy fails because it finds only one LIS, recursion is exponential and inefficient, sorting loses original order, but DP with length and count arrays correctly tracks LIS counts in O(n²).
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    DP with length and count arrays is standard for counting LIS [OK]
Hint: DP with length and count arrays counts LIS correctly [OK]
Common Mistakes:
  • Assuming greedy finds all LIS
  • Thinking sorting helps count LIS
  • Using pure recursion without pruning
3. What is the time complexity of the optimized LIS algorithm using binary search on an input array of length n?
medium
A. O(n log n)
B. O(n)
C. O(n²)
D. O(log n)

Solution

  1. Step 1: Identify outer loop complexity

    The algorithm iterates over all n elements once, so at least O(n).
  2. Step 2: Identify inner operation complexity

    For each element, binary search on tails array takes O(log n) time.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    n iterations x log n binary search -> O(n log n) [OK]
Hint: Binary search inside loop gives n log n [OK]
Common Mistakes:
  • Assuming O(n²) because of nested loops
  • Ignoring binary search complexity
  • Confusing with brute force exponential
4. The following code attempts to solve the Travelling Salesman Problem using bottom-up bitmask DP. Identify the bug that causes incorrect results on some inputs.
def tsp_bottom_up_buggy(dist):
    n = len(dist)
    ALL_VISITED = (1 << n) - 1
    dp = [[float('inf')] * n for _ in range(1 << n)]
    # BUG: dp[1][0] not initialized

    for mask in range(1 << n):
        for i in range(n):
            if mask & (1 << i):
                for j in range(n):
                    if mask & (1 << j) and i != j:
                        prev_mask = mask ^ (1 << i)
                        dp[mask][i] = min(dp[mask][i], dp[prev_mask][j] + dist[j][i])

    ans = float('inf')
    for i in range(1, n):
        ans = min(ans, dp[ALL_VISITED][i] + dist[i][0])
    return ans
medium
A. Missing base case initialization dp[1][0] = 0
B. Incorrect calculation of prev_mask = mask ^ (1 << i)
C. Line initializing dp array with float('inf')
D. Final loop over i in range(1, n) missing city 0

Solution

  1. Step 1: Check base case initialization

    The DP table must have dp[1][0] = 0 to represent starting at city 0 with only city 0 visited.
  2. Step 2: Consequence of missing base case

    Without this, all dp states remain infinite, so no valid path cost is computed, causing incorrect results.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Base case initialization is essential for DP correctness [OK]
Hint: Always initialize base case dp for starting city [OK]
Common Mistakes:
  • Forgetting dp[1][0] initialization leads to infinite costs
5. Suppose pairs can be reused multiple times in the chain (i.e., you can select the same pair multiple times if it fits). Which modification to the algorithm correctly finds the longest chain length under this new constraint?
hard
A. Use the same greedy approach sorting by end time, but allow pairs with start >= current_end
B. Use a bottom-up DP without sorting, checking all pairs for chain extension
C. Sort pairs by start time and greedily select pairs with earliest start repeatedly
D. Use dynamic programming with memoization to explore all sequences allowing reuse

Solution

  1. Step 1: Understand reuse breaks greedy assumptions

    Greedy relies on no reuse; allowing reuse requires exploring multiple sequences.
  2. Step 2: Use DP with memoization

    DP can handle repeated pairs by caching results for each state, ensuring correctness.
  3. Step 3: Reject naive greedy and unsorted DP

    Greedy fails with reuse; unsorted DP is inefficient and incorrect without sorting.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Memoized DP handles reuse correctly [OK]
Hint: Reuse breaks greedy; DP needed [OK]
Common Mistakes:
  • Trying to fix greedy by relaxing conditions
  • Ignoring sorting or memoization in DP
  • Assuming bottom-up DP without sorting suffices
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard