LeetCode 2028 - Find Missing Observations
The problem gives us the results of some dice rolls and asks us to reconstruct the missing ones. We have a total of n + m rolls of a standard 6-sided die.
Difficulty: 🟡 Medium
Topics: Array, Math, Simulation
Solution
Problem Understanding
The problem gives us the results of some dice rolls and asks us to reconstruct the missing ones.
We have a total of n + m rolls of a standard 6-sided die. Out of those rolls:
mvalues are known and stored in the arrayrollsnvalues are missing- the average of all
n + mrolls is exactlymean
Our task is to return any valid array of length n whose values are between 1 and 6, such that the average of the complete set of rolls becomes exactly mean.
Since the average is known, we can compute the total sum that all rolls together must have.
If:
- total number of rolls =
n + m - desired mean =
mean
then:
$$\text{required total sum} = (n + m) \times mean$$
We already know the sum of the observed rolls:
$$\text{observed sum} = \sum rolls$$
Therefore, the missing rolls must add up to:
$$\text{missing sum} = \text{required total sum} - \text{observed sum}$$
The challenge is determining whether it is possible to distribute this missing sum across exactly n dice rolls, where every value must stay within the valid dice range [1, 6].
The constraints are important:
1 <= n, m <= 10^5- each roll value is between
1and6
These constraints immediately rule out exponential or brute-force search approaches. We need a linear-time solution.
Several edge cases are especially important:
- The required missing sum may be too small. Since every die is at least
1, the minimum possible missing sum isn. - The required missing sum may be too large. Since every die is at most
6, the maximum possible missing sum is6 * n. - The missing sum may fit within the valid range, but distributing it incorrectly could produce invalid values outside
[1, 6].
The problem guarantees that if multiple valid answers exist, we may return any one of them.
Approaches
Brute Force Approach
A brute-force solution would try every possible combination of n dice rolls.
Each missing roll can take one of six values:
$$1, 2, 3, 4, 5, 6$$
So the total number of combinations is:
$$6^n$$
For every combination, we would compute the sum and check whether the average matches the required mean.
This approach is correct because it exhaustively explores every possible valid assignment of missing rolls. If a solution exists, brute force will eventually find it.
However, this method becomes completely infeasible for the given constraints. Since n can be as large as 10^5, even 6^{20} is already astronomically large, making exhaustive search impossible.
Optimal Approach
The key observation is that we do not actually care about the exact arrangement of values, only whether the required sum can be distributed across n dice.
Instead of searching through combinations, we can directly construct a valid answer.
First, compute the total missing sum:
$$\text{missing sum} = (n + m) \times mean - \sum rolls$$
Now we check feasibility:
- minimum possible sum with
ndice =n - maximum possible sum with
ndice =6n
If the missing sum falls outside this range, no solution exists.
Otherwise, we can construct the answer greedily.
One simple strategy is:
- start every missing roll at
1 - this uses a base sum of
n - distribute the remaining amount across the dice
- each die can receive at most
5extra points, since the maximum value is6
This guarantees that all values remain valid.
Because we only traverse the array once, the solution runs in linear time.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(6^n) | O(n) | Tries every possible combination of missing rolls |
| Optimal | O(n + m) | O(n) | Computes required sum directly and constructs a valid distribution |
Algorithm Walkthrough
- Compute the sum of the observed rolls.
We first calculate:
observed_sum = sum(rolls)
This tells us how much contribution already exists from the known dice rolls. 2. Compute the required total sum.
Since the average of all rolls must equal mean:
total_sum = (n + len(rolls)) * mean
- Compute the missing sum.
The missing rolls together must contribute:
missing_sum = total_sum - observed_sum
- Check whether the missing sum is feasible.
Every missing die must be between 1 and 6.
Therefore:
- minimum possible missing sum =
n - maximum possible missing sum =
6 * n
If:
missing_sum < n
or
missing_sum > 6 * n
then no valid solution exists, so we return an empty array.
5. Initialize all missing rolls to 1.
This guarantees all values are valid and gives us the minimum possible sum:
result = [1] * n
- Compute how much extra sum still needs to be distributed.
Since we already assigned 1 to every position:
remaining = missing_sum - n
- Distribute the remaining sum greedily.
Traverse the array and add as much as possible to each die.
Each die can receive at most 5 additional points because:
current value = 1
maximum value = 6
So:
add = min(5, remaining)
Then:
result[i] += add
remaining -= add
- Return the constructed array.
Once the remaining amount becomes zero, the array satisfies all constraints.
Why it works
The algorithm works because it constructs values while always maintaining valid dice bounds.
Starting with all ones guarantees the minimum feasible configuration. The remaining sum represents additional value that must be distributed. Since each die can increase by at most five more points, distributing greedily never violates the upper bound of six.
The feasibility check guarantees that the required sum lies within the achievable range. Therefore, if the check passes, the greedy construction will always succeed.
Python Solution
from typing import List
class Solution:
def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]:
observed_sum = sum(rolls)
total_rolls = len(rolls) + n
required_total = total_rolls * mean
missing_sum = required_total - observed_sum
if missing_sum < n or missing_sum > 6 * n:
return []
result = [1] * n
remaining = missing_sum - n
for i in range(n):
if remaining == 0:
break
extra = min(5, remaining)
result[i] += extra
remaining -= extra
return result
The implementation directly follows the algorithm described earlier.
First, we compute the observed sum using Python's built-in sum() function. Then we calculate the total required sum based on the desired mean.
The variable missing_sum represents how much total value the missing dice must contribute.
The feasibility check is critical. If the missing sum is smaller than n, even assigning all dice as 1 would exceed the target. If the missing sum is larger than 6 * n, even assigning all dice as 6 would not be enough.
After confirming feasibility, the code initializes every missing die to 1. This guarantees all values are valid from the beginning.
The remaining amount is then distributed greedily. Each die can absorb at most 5 additional points before reaching the maximum value of 6.
The loop stops early once all remaining value has been distributed.
Go Solution
func missingRolls(rolls []int, mean int, n int) []int {
observedSum := 0
for _, value := range rolls {
observedSum += value
}
totalRolls := len(rolls) + n
requiredTotal := totalRolls * mean
missingSum := requiredTotal - observedSum
if missingSum < n || missingSum > 6*n {
return []int{}
}
result := make([]int, n)
for i := 0; i < n; i++ {
result[i] = 1
}
remaining := missingSum - n
for i := 0; i < n && remaining > 0; i++ {
extra := remaining
if extra > 5 {
extra = 5
}
result[i] += extra
remaining -= extra
}
return result
}
The Go implementation follows the same logic as the Python version.
One small difference is that Go requires explicit initialization of slices. We create the result slice using make([]int, n) and manually assign each element to 1.
When no solution exists, the function returns an empty slice []int{}.
Integer overflow is not a concern because the maximum possible total sum is:
$$(10^5 + 10^5) \times 6 = 1.2 \times 10^6$$
which safely fits inside Go's integer type.
Worked Examples
Example 1
Input:
rolls = [3,2,4,3]
mean = 4
n = 2
Step 1: Compute observed sum
| Rolls | Sum |
|---|---|
| [3,2,4,3] | 12 |
Step 2: Compute required total sum
total rolls = 4 + 2 = 6
required total = 6 * 4 = 24
Step 3: Compute missing sum
missing sum = 24 - 12 = 12
Step 4: Feasibility check
minimum possible = 2
maximum possible = 12
Since 12 is within [2, 12], a solution exists.
Step 5: Initialize result
result = [1, 1]
remaining = 12 - 2 = 10
Step 6: Distribute remaining
| Index | Add | Result | Remaining |
|---|---|---|---|
| 0 | 5 | [6,1] | 5 |
| 1 | 5 | [6,6] | 0 |
Final answer:
[6,6]
Example 2
Input:
rolls = [1,5,6]
mean = 3
n = 4
Step 1: Compute observed sum
1 + 5 + 6 = 12
Step 2: Compute required total
total rolls = 3 + 4 = 7
required total = 7 * 3 = 21
Step 3: Compute missing sum
missing sum = 21 - 12 = 9
Step 4: Feasibility check
minimum possible = 4
maximum possible = 24
Valid.
Step 5: Initialize
result = [1,1,1,1]
remaining = 9 - 4 = 5
Step 6: Distribute remaining
| Index | Add | Result | Remaining |
|---|---|---|---|
| 0 | 5 | [6,1,1,1] | 0 |
Final answer:
[6,1,1,1]
This differs from the sample output, but it is still valid because any valid solution is accepted.
Example 3
Input:
rolls = [1,2,3,4]
mean = 6
n = 4
Step 1: Compute observed sum
1 + 2 + 3 + 4 = 10
Step 2: Compute required total
total rolls = 8
required total = 8 * 6 = 48
Step 3: Compute missing sum
missing sum = 48 - 10 = 38
Step 4: Feasibility check
maximum possible with 4 dice = 24
Since 38 > 24, no solution exists.
Return:
[]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n + m) | We sum the existing rolls once and traverse the result array once |
| Space | O(n) | The output array stores n missing values |
The algorithm performs only simple arithmetic and linear traversals. No nested loops or expensive data structures are used. Since constructing the answer itself requires storing n values, O(n) auxiliary space is unavoidable.
Test Cases
from typing import List
class Solution:
def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]:
observed_sum = sum(rolls)
total_rolls = len(rolls) + n
required_total = total_rolls * mean
missing_sum = required_total - observed_sum
if missing_sum < n or missing_sum > 6 * n:
return []
result = [1] * n
remaining = missing_sum - n
for i in range(n):
if remaining == 0:
break
extra = min(5, remaining)
result[i] += extra
remaining -= extra
return result
sol = Solution()
# Provided example 1
assert sorted(sol.missingRolls([3,2,4,3], 4, 2)) == [6,6]
# Provided example 2
result = sol.missingRolls([1,5,6], 3, 4)
assert len(result) == 4
assert sum(result) + sum([1,5,6]) == 21
# Provided example 3
assert sol.missingRolls([1,2,3,4], 6, 4) == []
# Minimum valid values
assert sol.missingRolls([1], 1, 1) == [1]
# Maximum valid values
assert sol.missingRolls([6], 6, 1) == [6]
# Impossible because sum too small
assert sol.missingRolls([6,6], 1, 2) == []
# Impossible because sum too large
assert sol.missingRolls([1,1], 6, 2) == []
# Single missing roll
assert sol.missingRolls([3,3,3], 3, 1) == [3]
# Large n stress test
result = sol.missingRolls([3] * 100000, 3, 100000)
assert len(result) == 100000
assert all(1 <= x <= 6 for x in result)
# Distribution requiring mixed values
result = sol.missingRolls([2,2,2], 4, 3)
assert len(result) == 3
assert sum(result) + 6 == 24
Test Summary
| Test | Why |
|---|---|
| Example 1 | Verifies a case where all missing dice become maximum values |
| Example 2 | Verifies general valid distribution |
| Example 3 | Verifies impossible large sum case |
[1], mean=1, n=1 |
Tests smallest valid input |
[6], mean=6, n=1 |
Tests largest single-die values |
| Impossible small sum | Ensures lower-bound feasibility check works |
| Impossible large sum | Ensures upper-bound feasibility check works |
| Single missing roll | Tests simplest reconstruction |
| Large stress test | Verifies linear performance |
| Mixed distribution case | Ensures correct sum distribution |
Edge Cases
One important edge case occurs when the missing sum is smaller than n. Since every die must be at least 1, the smallest possible total for n dice is exactly n. Any smaller target sum is impossible. A naive implementation that skips this feasibility check could accidentally produce zeros or negative values.
Another important edge case occurs when the missing sum exceeds 6 * n. Even if every missing die equals 6, the total would still be too small. Without this validation step, an implementation might incorrectly generate values larger than 6, violating the problem constraints.
A third important edge case involves large input sizes near 10^5. Recursive or brute-force approaches would fail either due to time complexity or stack limitations. The implemented solution avoids recursion entirely and processes the data with only linear traversals, making it efficient even at maximum constraints.
A subtle edge case appears when the remaining extra sum becomes zero before reaching the end of the array. In this situation, the remaining dice should stay at 1. The implementation handles this naturally because all positions start at 1, and the loop exits early once no additional value needs to be distributed.