1300. Sum of Mutated Array Closest to Target
Problem Description
You are given an integer array arr
and a target value target
. Your task is to find an integer value
that, when used to cap all elements in the array (replacing all numbers greater than value
with value
itself), produces a sum that is as close as possible to the target
.
Here's how the transformation works:
- Any element in
arr
that is greater thanvalue
gets replaced withvalue
- Elements less than or equal to
value
remain unchanged - After this transformation, calculate the sum of the modified array
The goal is to find the value
that minimizes the absolute difference between the resulting sum and the target
. If multiple values produce the same minimum difference, return the smallest such value
.
Important note: The answer value
doesn't have to be an element from the original array arr
- it can be any integer.
For example, if arr = [4, 9, 3]
and target = 10
:
- If we choose
value = 3
, the array becomes[3, 3, 3]
with sum = 9 - If we choose
value = 4
, the array becomes[4, 4, 3]
with sum = 11 - If we choose
value = 5
, the array becomes[4, 5, 3]
with sum = 12
The best choice would be value = 3
since the sum of 9 is closest to the target of 10.
Intuition
The key insight is that as we increase the value
from 0 to infinity, the sum of the modified array increases monotonically. When value
is very small, all elements get capped to this small value, resulting in a small sum. As value
increases, more elements remain unchanged, and the sum grows larger.
This monotonic behavior suggests we need to find the optimal value
where the sum is closest to the target. Since the sum changes continuously as value
changes, we can systematically check all possible values.
But what range should we check? We observe that once value
becomes greater than or equal to the maximum element in the array, increasing value
further won't change the sum anymore (since no elements would be capped). Therefore, we only need to check values from 0 to the maximum element in the array.
To efficiently calculate the sum for each value
, we can leverage sorting and prefix sums. If we sort the array first, all elements less than or equal to value
will be in a contiguous segment at the beginning. We can then:
- Use binary search to quickly find where
value
would fit in the sorted array - Use prefix sums to get the sum of elements that remain unchanged (those ≤
value
) - Calculate the contribution of capped elements as
(count of elements > value) × value
This approach transforms what could be an O(n) calculation for each value
into an O(log n) operation using binary search, making the overall solution more efficient.
Since we want the minimum value
in case of ties, iterating from smallest to largest naturally gives us the correct answer when we track the best result seen so far.
Learn more about Binary Search and Sorting patterns.
Solution Approach
Let's walk through the implementation step by step:
Step 1: Sort the array
arr.sort()
We sort the array to enable binary search and make it easier to identify which elements are greater than value
.
Step 2: Build prefix sum array
s = list(accumulate(arr, initial=0))
We create a prefix sum array where s[i]
represents the sum of the first i
elements. The initial=0
parameter ensures s[0] = 0
, making s
have length n+1
. This allows us to quickly calculate the sum of elements in any range.
Step 3: Initialize tracking variables
ans, diff = 0, inf
ans
: stores the bestvalue
found so fardiff
: tracks the minimum absolute difference from target
Step 4: Enumerate all possible values
for value in range(max(arr) + 1):
We check every integer from 0 to the maximum element in the array. Any value
beyond max(arr)
would leave all elements unchanged, so there's no need to check further.
Step 5: Find the split point using binary search
i = bisect_right(arr, value)
bisect_right
finds the index where value
would be inserted to keep the array sorted. This gives us:
- Elements at indices
[0, i)
are ≤value
(remain unchanged) - Elements at indices
[i, n)
are >value
(get capped tovalue
)
Step 6: Calculate the sum after transformation
d = abs(s[i] + (len(arr) - i) * value - target)
The total sum consists of:
s[i]
: sum of firsti
elements (those ≤value
)(len(arr) - i) * value
: contribution from capped elements
We calculate the absolute difference d
between this sum and the target.
Step 7: Update the best answer
if diff > d: diff = d ans = value
If we found a better (smaller) difference, we update both diff
and ans
. Since we iterate from small to large values, this naturally handles ties by keeping the smaller value
.
Time Complexity: O(M × log n) where M is the maximum value in the array and n is the array length. We iterate through M values and perform a binary search for each.
Space Complexity: O(n) for storing the sorted array and prefix sum array.
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 trace through the algorithm with arr = [4, 9, 3]
and target = 10
.
Step 1: Sort the array
arr = [3, 4, 9]
after sorting
Step 2: Build prefix sum array
s = [0, 3, 7, 16]
s[0] = 0
(initial value)s[1] = 0 + 3 = 3
s[2] = 3 + 4 = 7
s[3] = 7 + 9 = 16
Step 3: Initialize tracking variables
ans = 0
,diff = infinity
Step 4-7: Try each possible value from 0 to max(arr) = 9
When value = 0
:
bisect_right([3, 4, 9], 0) = 0
(all elements > 0)- Sum =
s[0] + (3 - 0) × 0 = 0 + 0 = 0
- Difference =
|0 - 10| = 10
- Update:
ans = 0
,diff = 10
When value = 1
:
bisect_right([3, 4, 9], 1) = 0
(all elements > 1)- Sum =
s[0] + (3 - 0) × 1 = 0 + 3 = 3
- Difference =
|3 - 10| = 7
- Update:
ans = 1
,diff = 7
When value = 2
:
bisect_right([3, 4, 9], 2) = 0
(all elements > 2)- Sum =
s[0] + (3 - 0) × 2 = 0 + 6 = 6
- Difference =
|6 - 10| = 4
- Update:
ans = 2
,diff = 4
When value = 3
:
bisect_right([3, 4, 9], 3) = 1
(element 3 ≤ 3, others > 3)- Sum =
s[1] + (3 - 1) × 3 = 3 + 6 = 9
- Difference =
|9 - 10| = 1
- Update:
ans = 3
,diff = 1
When value = 4
:
bisect_right([3, 4, 9], 4) = 2
(elements 3,4 ≤ 4, element 9 > 4)- Sum =
s[2] + (3 - 2) × 4 = 7 + 4 = 11
- Difference =
|11 - 10| = 1
- No update (diff = 1 is not better)
When value = 5
:
bisect_right([3, 4, 9], 5) = 2
(elements 3,4 ≤ 5, element 9 > 5)- Sum =
s[2] + (3 - 2) × 5 = 7 + 5 = 12
- Difference =
|12 - 10| = 2
- No update (diff = 2 is worse)
Continuing through values 6-9, the differences keep getting worse.
Final Answer: 3
The algorithm correctly identifies that value = 3
produces a sum of 9, which is closest to the target of 10. When there was a tie (both value = 3
and value = 4
had difference = 1), the algorithm kept the smaller value as required.
Solution Implementation
1from typing import List
2from itertools import accumulate
3from bisect import bisect_right
4
5
6class Solution:
7 def findBestValue(self, arr: List[int], target: int) -> int:
8 # Sort the array for binary search and prefix sum calculation
9 arr.sort()
10
11 # Create prefix sum array with initial 0 for easier indexing
12 # prefix_sums[i] represents sum of first i elements
13 prefix_sums = list(accumulate(arr, initial=0))
14
15 # Initialize variables to track the best value and minimum difference
16 best_value = 0
17 min_difference = float('inf')
18
19 # Try all possible values from 0 to max element in array
20 for candidate_value in range(max(arr) + 1):
21 # Find the index where candidate_value would be inserted
22 # This gives us the count of elements <= candidate_value
23 index = bisect_right(arr, candidate_value)
24
25 # Calculate the sum after replacing all elements > candidate_value
26 # Sum = (sum of elements <= candidate_value) + (count of elements > candidate_value) * candidate_value
27 current_sum = prefix_sums[index] + (len(arr) - index) * candidate_value
28
29 # Calculate absolute difference from target
30 current_difference = abs(current_sum - target)
31
32 # Update best value if we found a smaller difference
33 if current_difference < min_difference:
34 min_difference = current_difference
35 best_value = candidate_value
36
37 return best_value
38
1class Solution {
2 public int findBestValue(int[] arr, int target) {
3 // Sort the array to enable binary search and prefix sum calculation
4 Arrays.sort(arr);
5 int n = arr.length;
6
7 // Create prefix sum array where prefixSum[i] = sum of first i elements
8 int[] prefixSum = new int[n + 1];
9 int maxValue = 0;
10
11 // Build prefix sum array and find maximum value in the array
12 for (int i = 0; i < n; i++) {
13 prefixSum[i + 1] = prefixSum[i] + arr[i];
14 maxValue = Math.max(maxValue, arr[i]);
15 }
16
17 // Try all possible values from 0 to maxValue to find the best one
18 int bestValue = 0;
19 int minDifference = Integer.MAX_VALUE;
20
21 for (int value = 0; value <= maxValue; value++) {
22 // Find the index where elements start to be greater than current value
23 int index = binarySearchUpperBound(arr, value);
24
25 // Calculate sum: elements before index remain unchanged,
26 // elements from index onwards are replaced with value
27 int currentSum = prefixSum[index] + (n - index) * value;
28 int difference = Math.abs(currentSum - target);
29
30 // Update best value if current difference is smaller
31 if (difference < minDifference) {
32 minDifference = difference;
33 bestValue = value;
34 }
35 }
36
37 return bestValue;
38 }
39
40 /**
41 * Binary search to find the first index where arr[index] > x
42 * Returns the number of elements <= x
43 */
44 private int binarySearchUpperBound(int[] arr, int x) {
45 int left = 0;
46 int right = arr.length;
47
48 while (left < right) {
49 int mid = left + (right - left) / 2;
50
51 if (arr[mid] > x) {
52 // Mid element is greater than x, search in left half
53 right = mid;
54 } else {
55 // Mid element is <= x, search in right half
56 left = mid + 1;
57 }
58 }
59
60 return left;
61 }
62}
63
1class Solution {
2public:
3 int findBestValue(vector<int>& arr, int target) {
4 // Sort the array in ascending order
5 sort(arr.begin(), arr.end());
6
7 int n = arr.size();
8
9 // Build prefix sum array
10 // prefixSum[i] represents the sum of first i elements
11 vector<int> prefixSum(n + 1, 0);
12 int maxValue = 0;
13
14 for (int i = 0; i < n; ++i) {
15 prefixSum[i + 1] = prefixSum[i] + arr[i];
16 maxValue = max(maxValue, arr[i]);
17 }
18
19 // Find the best value that minimizes the difference
20 int bestValue = 0;
21 int minDifference = INT_MAX;
22
23 // Try all possible values from 0 to maximum element
24 for (int value = 0; value <= maxValue; ++value) {
25 // Find the first position where arr[pos] > value
26 // All elements from this position onwards will be replaced by 'value'
27 int pos = upper_bound(arr.begin(), arr.end(), value) - arr.begin();
28
29 // Calculate the sum after replacement:
30 // - Keep elements arr[0] to arr[pos-1] as they are (their sum is prefixSum[pos])
31 // - Replace elements arr[pos] to arr[n-1] with 'value' (total: (n - pos) * value)
32 int currentSum = prefixSum[pos] + (n - pos) * value;
33 int difference = abs(currentSum - target);
34
35 // Update the best value if current difference is smaller
36 if (difference < minDifference) {
37 minDifference = difference;
38 bestValue = value;
39 }
40 }
41
42 return bestValue;
43 }
44};
45
1function findBestValue(arr: number[], target: number): number {
2 // Sort the array in ascending order
3 arr.sort((a, b) => a - b);
4
5 const n = arr.length;
6
7 // Build prefix sum array
8 // prefixSum[i] represents the sum of first i elements
9 const prefixSum: number[] = new Array(n + 1).fill(0);
10 let maxValue = 0;
11
12 for (let i = 0; i < n; i++) {
13 prefixSum[i + 1] = prefixSum[i] + arr[i];
14 maxValue = Math.max(maxValue, arr[i]);
15 }
16
17 // Find the best value that minimizes the difference
18 let bestValue = 0;
19 let minDifference = Number.MAX_SAFE_INTEGER;
20
21 // Try all possible values from 0 to maximum element
22 for (let value = 0; value <= maxValue; value++) {
23 // Find the first position where arr[pos] > value
24 // All elements from this position onwards will be replaced by 'value'
25 const pos = upperBound(arr, value);
26
27 // Calculate the sum after replacement:
28 // - Keep elements arr[0] to arr[pos-1] as they are (their sum is prefixSum[pos])
29 // - Replace elements arr[pos] to arr[n-1] with 'value' (total: (n - pos) * value)
30 const currentSum = prefixSum[pos] + (n - pos) * value;
31 const difference = Math.abs(currentSum - target);
32
33 // Update the best value if current difference is smaller
34 if (difference < minDifference) {
35 minDifference = difference;
36 bestValue = value;
37 }
38 }
39
40 return bestValue;
41}
42
43// Helper function to find the first position where arr[pos] > value
44// Returns the index of the first element greater than value
45function upperBound(arr: number[], value: number): number {
46 let left = 0;
47 let right = arr.length;
48
49 while (left < right) {
50 const mid = Math.floor((left + right) / 2);
51 if (arr[mid] <= value) {
52 left = mid + 1;
53 } else {
54 right = mid;
55 }
56 }
57
58 return left;
59}
60
Time and Space Complexity
The time complexity of this code is O(n × log n + m × log n)
where n
is the length of the array arr
and m
is the maximum value in the array.
Breaking down the operations:
arr.sort()
:O(n × log n)
for sorting the arraylist(accumulate(arr, initial=0))
:O(n)
to create the prefix sum array- The for loop runs from
0
tomax(arr)
, which isO(m)
iterations - Inside each iteration,
bisect_right(arr, value)
performs binary search:O(log n)
- Other operations inside the loop are
O(1)
- Total for the loop:
O(m × log n)
Overall time complexity: O(n × log n + m × log n)
. When m
(max value) is considered as O(n)
in typical cases or bounded by the problem constraints, this can be simplified to O(n × log n)
.
The space complexity is O(n)
:
- The sorted array is done in-place (though in Python,
arr.sort()
might useO(n)
auxiliary space) - The prefix sum array
s
requiresO(n+1) = O(n)
space - Other variables (
ans
,diff
,value
,i
,d
) useO(1)
space
Therefore, the total space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Inefficient Search Range
Problem: The current solution iterates through all values from 0 to max(arr)
, which can be inefficient when the array contains very large numbers. For example, if arr = [1, 2, 100000]
and target = 5
, we'd unnecessarily check 100,001 values when the optimal answer is likely very small.
Solution: Use binary search on the value space instead of linear iteration:
def findBestValue(self, arr: List[int], target: int) -> int:
arr.sort()
n = len(arr)
prefix_sums = list(accumulate(arr, initial=0))
def calculate_sum(value):
index = bisect_right(arr, value)
return prefix_sums[index] + (n - index) * value
# Binary search on the value space
left, right = 0, max(arr)
# Find the point where sum transitions from < target to >= target
while left < right:
mid = (left + right) // 2
if calculate_sum(mid) < target:
left = mid + 1
else:
right = mid
# Check both left-1 and left to find the closest
if left > 0 and abs(calculate_sum(left - 1) - target) <= abs(calculate_sum(left) - target):
return left - 1
return left
Pitfall 2: Mathematical Optimization Opportunity Missed
Problem: The solution doesn't leverage the fact that once we fix how many elements to cap, we can directly calculate the optimal capping value mathematically.
Solution: For each possible number of elements to cap, calculate the optimal value directly:
def findBestValue(self, arr: List[int], target: int) -> int:
arr.sort()
n = len(arr)
prefix_sum = 0
for i in range(n):
# Calculate optimal value if we cap elements from index i onwards
# optimal_value = (target - prefix_sum) / (n - i)
optimal = (target - prefix_sum) / (n - i)
# If optimal value is less than current element, we found our answer
if optimal < arr[i]:
# Check both floor and ceiling of optimal
low = int(optimal)
high = low + 1
sum_low = prefix_sum + low * (n - i)
sum_high = prefix_sum + high * (n - i)
if abs(sum_low - target) <= abs(sum_high - target):
return low
return high
prefix_sum += arr[i]
# If we reach here, no capping needed
return arr[-1]
Pitfall 3: Edge Case with Empty Array or Zero Target
Problem: The solution doesn't explicitly handle edge cases like empty arrays or when the target is 0 or negative.
Solution: Add validation at the beginning:
def findBestValue(self, arr: List[int], target: int) -> int:
if not arr:
return 0
# If target is 0 or negative, best value is 0
if target <= 0:
return 0
# If sum of array is already less than or equal to target
if sum(arr) <= target:
return max(arr)
# Continue with main algorithm...
These optimizations reduce time complexity from O(M × log n) to O(n log n) or even O(n) in the mathematical approach, making the solution much more efficient for arrays with large values.
Which of the following uses divide and conquer strategy?
Recommended Readings
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
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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!