441. Arranging Coins
Problem Description
You are given n
coins and need to build a staircase with them. The staircase follows a specific pattern where:
- Row 1 contains exactly 1 coin
- Row 2 contains exactly 2 coins
- Row 3 contains exactly 3 coins
- And so on...
Each row i
must contain exactly i
coins. You can only count a row as complete if it has the exact required number of coins. The last row might not have enough coins to be complete, in which case it doesn't count toward your answer.
Your task is to determine how many complete rows you can build with the given n
coins.
For example:
- If
n = 5
, you can build 2 complete rows (row 1 uses 1 coin, row 2 uses 2 coins, leaving 2 coins which isn't enough to complete row 3 that needs 3 coins) - If
n = 8
, you can build 3 complete rows (row 1 uses 1 coin, row 2 uses 2 coins, row 3 uses 3 coins, totaling 6 coins, leaving 2 coins which isn't enough for row 4)
The solution uses a mathematical formula derived from the fact that the sum of the first k
natural numbers is k * (k + 1) / 2
. By solving the inequality k * (k + 1) / 2 ≤ n
for k
, we get the formula k = int(sqrt(2) * sqrt(n + 0.125) - 0.5)
, which directly calculates the number of complete rows.
Intuition
The key insight is recognizing that this is a problem about finding the largest triangular number that doesn't exceed n
. When we build the staircase, we're essentially using 1 + 2 + 3 + ... + k
coins for k
complete rows.
The sum of the first k
natural numbers follows the formula k * (k + 1) / 2
. So we need to find the largest k
where k * (k + 1) / 2 ≤ n
.
To solve this, we can rearrange the inequality:
k * (k + 1) / 2 ≤ n
k² + k ≤ 2n
k² + k - 2n ≤ 0
This is a quadratic inequality. Using the quadratic formula where a = 1
, b = 1
, and c = -2n
, we get:
k = (-1 ± sqrt(1 + 8n)) / 2
Since k
must be positive (we can't have negative rows), we take the positive root:
k = (-1 + sqrt(1 + 8n)) / 2
This can be rewritten as:
k = sqrt(2n + 0.25) - 0.5
k = sqrt(2) * sqrt(n + 0.125) - 0.5
Since we need the number of complete rows, we take the floor of this value using int()
. This mathematical formula directly gives us the answer in constant time without needing to iterate or use binary search.
The beauty of this approach is that it transforms what seems like a counting problem into a mathematical equation that can be solved instantly, regardless of how large n
is.
Solution Approach
The implementation is remarkably concise, using a direct mathematical formula to solve the problem in constant time.
def arrangeCoins(self, n: int) -> int:
return int(math.sqrt(2) * math.sqrt(n + 0.125) - 0.5)
Let's break down the implementation step by step:
-
Mathematical Formula Application: The formula
sqrt(2) * sqrt(n + 0.125) - 0.5
is derived from solving the quadratic inequalityk * (k + 1) / 2 ≤ n
fork
. -
Breaking Down the Computation:
- First, we add
0.125
(which is1/8
) ton
. This comes from rewriting the discriminant1 + 8n
as8(n + 1/8)
. - We take the square root of
(n + 0.125)
. - We multiply by
sqrt(2)
to account for the factoring of the discriminant. - We subtract
0.5
to complete the quadratic formula transformation.
- First, we add
-
Floor Operation: The
int()
function effectively performs a floor operation on the result, giving us the largest integerk
that satisfies our constraint. This ensures we only count complete rows. -
Time and Space Complexity:
- Time Complexity:
O(1)
- We perform a constant number of mathematical operations regardless of input size. - Space Complexity:
O(1)
- We only use a fixed amount of extra space for the calculation.
- Time Complexity:
The elegance of this solution lies in avoiding iterative approaches or binary search. Instead of checking row by row or searching for the answer, we directly compute it using the mathematical relationship between the number of coins and the number of complete rows that can be formed.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with n = 11
coins.
Step 1: Understanding what we're looking for
We need to find how many complete staircase rows we can build. Each row i
needs exactly i
coins:
- Row 1: 1 coin
- Row 2: 2 coins
- Row 3: 3 coins
- Row 4: 4 coins
- And so on...
Step 2: Apply the mathematical formula
Using our formula: k = int(sqrt(2) * sqrt(n + 0.125) - 0.5)
Let's substitute n = 11
:
- First, calculate
n + 0.125 = 11 + 0.125 = 11.125
- Take the square root:
sqrt(11.125) ≈ 3.335
- Multiply by
sqrt(2)
:sqrt(2) * 3.335 ≈ 1.414 * 3.335 ≈ 4.716
- Subtract 0.5:
4.716 - 0.5 = 4.216
- Apply
int()
to get the floor:int(4.216) = 4
Step 3: Verify the result With 4 complete rows, we use:
- Row 1: 1 coin
- Row 2: 2 coins
- Row 3: 3 coins
- Row 4: 4 coins
- Total: 1 + 2 + 3 + 4 = 10 coins
We've used 10 coins out of 11, leaving 1 coin. Row 5 would need 5 coins, but we only have 1 remaining, so we can't complete it.
Therefore, we can build 4 complete rows with 11 coins.
Why the formula works:
The formula solves the inequality k * (k + 1) / 2 ≤ n
. For our example:
- 4 * 5 / 2 = 10 ≤ 11 ✓ (4 rows work)
- 5 * 6 / 2 = 15 > 11 ✗ (5 rows would need too many coins)
The mathematical formula directly finds this breaking point without any iteration.
Solution Implementation
1import math
2
3class Solution:
4 def arrangeCoins(self, n: int) -> int:
5 """
6 Find the maximum number of complete staircase rows that can be formed with n coins.
7
8 Uses the mathematical formula derived from the sum of arithmetic sequence:
9 1 + 2 + 3 + ... + k = k * (k + 1) / 2 <= n
10
11 Solving for k using the quadratic formula:
12 k^2 + k - 2n <= 0
13 k <= (-1 + sqrt(1 + 8n)) / 2
14
15 The formula simplifies to: k = sqrt(2 * (n + 1/8)) - 1/2
16
17 Args:
18 n: The total number of coins available
19
20 Returns:
21 The number of complete staircase rows that can be formed
22 """
23 # Calculate the maximum number of complete rows using the closed-form solution
24 # sqrt(2) * sqrt(n + 0.125) - 0.5 is equivalent to sqrt(2 * (n + 1/8)) - 1/2
25 max_complete_rows = int(math.sqrt(2) * math.sqrt(n + 0.125) - 0.5)
26
27 return max_complete_rows
28
1class Solution {
2 /**
3 * Finds the number of complete rows in a staircase pattern using n coins.
4 * Uses the mathematical formula derived from the sum of arithmetic sequence.
5 *
6 * The problem asks for the largest k where k*(k+1)/2 <= n
7 * Solving the quadratic inequality: k^2 + k - 2n <= 0
8 * Using the quadratic formula: k = (-1 + sqrt(1 + 8n)) / 2
9 * This can be rewritten as: k = sqrt(2) * sqrt(n + 1/8) - 1/2
10 *
11 * @param n The total number of coins available
12 * @return The number of complete staircase rows that can be formed
13 */
14 public int arrangeCoins(int n) {
15 // Calculate the mathematical formula for finding complete rows
16 // sqrt(2) * sqrt(n + 0.125) - 0.5 gives the exact solution
17 // Cast to int to get the floor value (number of complete rows)
18 double sqrtOfTwo = Math.sqrt(2);
19 double adjustedValue = n + 0.125; // n + 1/8
20 double sqrtOfAdjustedValue = Math.sqrt(adjustedValue);
21 double result = sqrtOfTwo * sqrtOfAdjustedValue - 0.5;
22
23 return (int) result;
24 }
25}
26
1using LL = long long; // Define long long type alias
2
3class Solution {
4public:
5 int arrangeCoins(int n) {
6 // Binary search to find the maximum number of complete staircase rows
7 // We're looking for the largest k where k*(k+1)/2 <= n
8 LL left = 1, right = n;
9
10 while (left < right) {
11 // Calculate mid point, using (left + right + 1) / 2 to avoid infinite loop
12 // The +1 ensures we round up when left and right are adjacent
13 LL mid = left + ((right - left + 1) >> 1); // Equivalent to (left + right + 1) / 2
14
15 // Calculate sum of arithmetic sequence: 1 + 2 + 3 + ... + mid
16 // Formula: sum = mid * (mid + 1) / 2
17 LL sum = (mid * (mid + 1)) >> 1; // Using bit shift for division by 2
18
19 // If n is less than the required coins for mid rows,
20 // search in the left half
21 if (n < sum) {
22 right = mid - 1;
23 }
24 // Otherwise, mid rows can be formed, search in the right half
25 else {
26 left = mid;
27 }
28 }
29
30 // Return the maximum number of complete rows
31 return left;
32 }
33};
34
1function arrangeCoins(n: number): number {
2 // Binary search to find the maximum number of complete staircase rows
3 // We're looking for the largest k where k*(k+1)/2 <= n
4 let left: number = 1;
5 let right: number = n;
6
7 while (left < right) {
8 // Calculate mid point, using Math.floor((left + right + 1) / 2) to avoid infinite loop
9 // The +1 ensures we round up when left and right are adjacent
10 const mid: number = left + Math.floor((right - left + 1) / 2);
11
12 // Calculate sum of arithmetic sequence: 1 + 2 + 3 + ... + mid
13 // Formula: sum = mid * (mid + 1) / 2
14 const sum: number = Math.floor((mid * (mid + 1)) / 2);
15
16 // If n is less than the required coins for mid rows,
17 // search in the left half
18 if (n < sum) {
19 right = mid - 1;
20 }
21 // Otherwise, mid rows can be formed, search in the right half
22 else {
23 left = mid;
24 }
25 }
26
27 // Return the maximum number of complete rows
28 return left;
29}
30
Time and Space Complexity
Time Complexity: O(1)
The solution uses a mathematical formula to directly calculate the number of complete rows that can be formed with n
coins. The formula int(math.sqrt(2) * math.sqrt(n + 0.125) - 0.5)
is derived from solving the quadratic equation for the sum of an arithmetic sequence.
Given that 1 + 2 + 3 + ... + k = k(k+1)/2 ≤ n
, we need to find the largest k
such that k(k+1)/2 ≤ n
. Solving the quadratic equation k² + k - 2n = 0
using the quadratic formula gives us k = (-1 + sqrt(1 + 8n)) / 2
. The code implements this formula in a slightly rearranged form.
All operations involved (math.sqrt()
, multiplication, addition, subtraction, and integer conversion) are constant time operations, making the overall time complexity O(1)
.
Space Complexity: O(1)
The solution only uses a constant amount of extra space to store intermediate calculation results and the final return value. No additional data structures or recursive calls are used, resulting in constant space complexity.
Common Pitfalls
1. Floating-Point Precision Issues
The mathematical formula involves square root operations and decimal arithmetic, which can lead to precision errors for very large values of n
. When n
approaches the maximum integer value, the intermediate calculations might lose precision, potentially causing the formula to return an incorrect result that's off by 1.
Example of the issue:
# For very large n, floating-point errors might accumulate
n = 2**31 - 1 # Maximum 32-bit integer
result = int(math.sqrt(2) * math.sqrt(n + 0.125) - 0.5)
# The result might be off by 1 due to precision loss
Solution: Add a verification step to ensure correctness:
def arrangeCoins(self, n: int) -> int:
k = int(math.sqrt(2) * math.sqrt(n + 0.125) - 0.5)
# Verify the result and adjust if necessary
# Check if k is valid (sum of 1 to k should be <= n)
if k * (k + 1) // 2 > n:
return k - 1
# Check if we can actually fit one more row
elif (k + 1) * (k + 2) // 2 <= n:
return k + 1
return k
2. Integer Overflow in Alternative Implementations
If someone tries to verify or implement this using the direct sum formula k * (k + 1) / 2
, they might encounter integer overflow when calculating k * (k + 1)
for large values of k
.
Example of the issue:
# This verification might overflow for large k
def verify_result(k, n):
total = k * (k + 1) // 2 # This multiplication can overflow
return total <= n
Solution: Use a comparison that avoids overflow:
def verify_result(k, n):
# Rearrange to avoid overflow: k*(k+1)/2 <= n becomes k*(k+1) <= 2*n
# But even better, subtract as we go
remaining = n
for i in range(1, k + 1):
remaining -= i
if remaining < 0:
return False
return True
3. Direct Formula Application Without Understanding
Blindly applying the formula without understanding its derivation can lead to mistakes when modifying the problem or debugging edge cases.
Solution: Always keep a simple iterative solution as a fallback or for testing:
def arrangeCoins_iterative(self, n: int) -> int:
# Simple O(sqrt(n)) solution for verification
k = 0
while n >= k + 1:
k += 1
n -= k
return k
This can be used to verify the mathematical solution for test cases and helps understand the problem intuitively.
Which data structure is used to implement priority queue?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!