questions A (01-matrix - avoid-flood)
This commit is contained in:
170
backend/data/questions/arranging-coins.yaml
Normal file
170
backend/data/questions/arranging-coins.yaml
Normal file
@@ -0,0 +1,170 @@
|
||||
title: Arranging Coins
|
||||
slug: arranging-coins
|
||||
difficulty: easy
|
||||
leetcode_id: 441
|
||||
leetcode_url: https://leetcode.com/problems/arranging-coins/
|
||||
categories:
|
||||
- math
|
||||
- binary-search
|
||||
patterns:
|
||||
- binary-search
|
||||
|
||||
description: |
|
||||
You have `n` coins and you want to build a staircase with these coins. The staircase consists of `k` rows where the i<sup>th</sup> row has exactly `i` coins. The last row of the staircase **may be** incomplete.
|
||||
|
||||
Given the integer `n`, return *the number of **complete rows** of the staircase you will build*.
|
||||
|
||||
constraints: |
|
||||
- `1 <= n <= 2^31 - 1`
|
||||
|
||||
examples:
|
||||
- input: "n = 5"
|
||||
output: "2"
|
||||
explanation: "Because the 3rd row is incomplete, we return 2."
|
||||
- input: "n = 8"
|
||||
output: "3"
|
||||
explanation: "Because the 4th row is incomplete, we return 3."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
Imagine stacking coins row by row: the 1<sup>st</sup> row needs 1 coin, the 2<sup>nd</sup> row needs 2 coins, the 3<sup>rd</sup> row needs 3 coins, and so on.
|
||||
|
||||
The total number of coins needed for `k` complete rows is `1 + 2 + 3 + ... + k`, which equals `k(k+1)/2` (the triangular number formula).
|
||||
|
||||
Think of it like this: we're searching for the **largest value of k** such that the sum of the first k natural numbers does not exceed n. This is essentially asking: "What's the biggest staircase I can fully build with n coins?"
|
||||
|
||||
Since the sum `k(k+1)/2` increases monotonically with k, we can use **binary search** to efficiently find the answer. Alternatively, we can solve the quadratic equation directly using the quadratic formula for an O(1) mathematical solution.
|
||||
|
||||
approach: |
|
||||
We can solve this using **Binary Search** on the number of rows:
|
||||
|
||||
**Step 1: Define the search space**
|
||||
|
||||
- `left`: Set to `1` (minimum possible complete rows)
|
||||
- `right`: Set to `n` (maximum possible rows, though we'll never need this many)
|
||||
|
||||
|
||||
|
||||
**Step 2: Binary search for the largest valid k**
|
||||
|
||||
- Calculate `mid` as the midpoint of the current range
|
||||
- Compute the coins needed for `mid` complete rows: `mid * (mid + 1) / 2`
|
||||
- If coins needed equals `n`, we found an exact match - return `mid`
|
||||
- If coins needed is less than `n`, we might be able to fit more rows - search right
|
||||
- If coins needed is greater than `n`, we need fewer rows - search left
|
||||
|
||||
|
||||
|
||||
**Step 3: Return the result**
|
||||
|
||||
- When the loop ends, `right` contains the largest k where `k(k+1)/2 <= n`
|
||||
- Return `right` as the number of complete rows
|
||||
|
||||
|
||||
|
||||
The binary search efficiently narrows down the answer in O(log n) time instead of simulating the stacking process.
|
||||
|
||||
common_pitfalls:
|
||||
- title: The Simulation Trap
|
||||
description: |
|
||||
A naive approach is to simulate building the staircase row by row:
|
||||
- Start with row 1, subtract 1 coin
|
||||
- Move to row 2, subtract 2 coins
|
||||
- Continue until you can't complete a row
|
||||
|
||||
While correct, this takes **O(sqrt(n)) time** because you'll build approximately sqrt(2n) rows. For `n = 2^31 - 1`, that's about 65,000 iterations. Binary search does it in about 31 iterations.
|
||||
wrong_approach: "Simulate stacking row by row"
|
||||
correct_approach: "Binary search on the number of rows"
|
||||
|
||||
- title: Integer Overflow
|
||||
description: |
|
||||
When computing `mid * (mid + 1) / 2`, the multiplication can overflow if `mid` is large. With `n` up to `2^31 - 1`, `mid` could be around `65,000`, and `mid * (mid + 1)` could exceed 32-bit integer limits.
|
||||
|
||||
In Python this isn't an issue due to arbitrary precision integers, but in languages like Java or C++, you need to use `long` or divide before multiplying: `mid / 2 * (mid + 1)` or `mid * (mid + 1) / 2` with long types.
|
||||
wrong_approach: "Use 32-bit integers without overflow protection"
|
||||
correct_approach: "Use 64-bit integers or Python's arbitrary precision"
|
||||
|
||||
- title: Off-by-One Errors
|
||||
description: |
|
||||
Binary search boundary conditions can be tricky. A common mistake is returning `left` instead of `right`, or using the wrong comparison operator.
|
||||
|
||||
The key insight: we want the **largest** k where `k(k+1)/2 <= n`. When the search converges, `right` holds this value because we move `right = mid - 1` when we have too many coins.
|
||||
wrong_approach: "Incorrect boundary handling in binary search"
|
||||
correct_approach: "Carefully track which boundary holds the answer"
|
||||
|
||||
key_takeaways:
|
||||
- "**Triangular number formula**: The sum `1 + 2 + ... + k = k(k+1)/2` appears frequently in problems involving sequential additions"
|
||||
- "**Binary search on answer**: When searching for the largest/smallest value satisfying a condition, binary search is often applicable"
|
||||
- "**Mathematical solutions**: Some binary search problems have closed-form solutions using algebra (quadratic formula here)"
|
||||
- "**Monotonic property**: Binary search works because `k(k+1)/2` strictly increases with k - if k rows need more than n coins, so will k+1 rows"
|
||||
|
||||
time_complexity: "O(log n). Binary search halves the search space each iteration, and the search space is at most n."
|
||||
space_complexity: "O(1). We only use a constant number of variables (`left`, `right`, `mid`, `coins_needed`)."
|
||||
|
||||
solutions:
|
||||
- approach_name: Binary Search
|
||||
is_optimal: true
|
||||
code: |
|
||||
def arrange_coins(n: int) -> int:
|
||||
left, right = 1, n
|
||||
|
||||
while left <= right:
|
||||
mid = left + (right - left) // 2
|
||||
# Coins needed for mid complete rows
|
||||
coins_needed = mid * (mid + 1) // 2
|
||||
|
||||
if coins_needed == n:
|
||||
# Exact fit - mid rows use exactly n coins
|
||||
return mid
|
||||
elif coins_needed < n:
|
||||
# We have coins left over, try more rows
|
||||
left = mid + 1
|
||||
else:
|
||||
# Not enough coins, try fewer rows
|
||||
right = mid - 1
|
||||
|
||||
# right is the largest k where k(k+1)/2 <= n
|
||||
return right
|
||||
explanation: |
|
||||
**Time Complexity:** O(log n) - Binary search on the range [1, n].
|
||||
|
||||
**Space Complexity:** O(1) - Only constant extra space used.
|
||||
|
||||
We binary search for the largest k such that k(k+1)/2 <= n. Each iteration halves the search space, and we use the triangular number formula to compute coins needed in O(1).
|
||||
|
||||
- approach_name: Mathematical (Quadratic Formula)
|
||||
is_optimal: true
|
||||
code: |
|
||||
import math
|
||||
|
||||
def arrange_coins(n: int) -> int:
|
||||
# We need the largest k where k(k+1)/2 <= n
|
||||
# Solving k^2 + k - 2n = 0 using quadratic formula:
|
||||
# k = (-1 + sqrt(1 + 8n)) / 2
|
||||
return int((-1 + math.sqrt(1 + 8 * n)) / 2)
|
||||
explanation: |
|
||||
**Time Complexity:** O(1) - Direct calculation using the quadratic formula.
|
||||
|
||||
**Space Complexity:** O(1) - Only constant extra space used.
|
||||
|
||||
We solve k(k+1)/2 <= n algebraically. Rearranging to k^2 + k - 2n <= 0 and applying the quadratic formula gives k = (-1 + sqrt(1 + 8n)) / 2. Taking the floor gives the largest valid k.
|
||||
|
||||
- approach_name: Linear Simulation
|
||||
is_optimal: false
|
||||
code: |
|
||||
def arrange_coins(n: int) -> int:
|
||||
rows = 0
|
||||
coins_remaining = n
|
||||
|
||||
# Build rows one by one until we can't complete one
|
||||
while coins_remaining >= rows + 1:
|
||||
rows += 1
|
||||
coins_remaining -= rows
|
||||
|
||||
return rows
|
||||
explanation: |
|
||||
**Time Complexity:** O(sqrt(n)) - We build approximately sqrt(2n) rows.
|
||||
|
||||
**Space Complexity:** O(1) - Only constant extra space used.
|
||||
|
||||
This simulates the stacking process directly. While intuitive and correct, it's slower than the binary search or mathematical approaches. Included to show the progression from brute force to optimal solution.
|
||||
Reference in New Issue
Block a user