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
arrthat is greater thanvaluegets replaced withvalue - Elements less than or equal to
valueremain 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. This creates a "false, false, ..., true, true, ..." pattern that's perfect for the binary search template.
We can define a feasible function: "Does this value produce a sum >= target?" Once we find the first value where the sum meets or exceeds the target, we know we're at the boundary. The optimal answer must be either this value or the one just before it.
To efficiently calculate the sum for any value, we use two techniques:
- Sort the array so we can use binary search (bisect_right) to quickly find how many elements are ≤ value
- Build prefix sums to calculate the sum of unchanged elements in O(1)
This transforms the problem into the standard binary search template pattern: find the first value where calculate_sum(value) >= target, then check both candidates.
Learn more about Binary Search and Sorting patterns.
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 arr.sort()
9 n = len(arr)
10 prefix_sums = list(accumulate(arr, initial=0))
11
12 def calculate_sum(value):
13 # Find how many elements are <= value
14 index = bisect_right(arr, value)
15 # Sum = unchanged elements + capped elements
16 return prefix_sums[index] + (n - index) * value
17
18 # Binary search template: find first value where sum >= target
19 left, right = 0, max(arr)
20 first_true_index = -1
21
22 while left <= right:
23 mid = (left + right) // 2
24 if calculate_sum(mid) >= target: # feasible condition
25 first_true_index = mid
26 right = mid - 1
27 else:
28 left = mid + 1
29
30 # Edge case: no value produces sum >= target
31 if first_true_index == -1:
32 return max(arr)
33
34 # Edge case: even value 0 produces sum >= target
35 if first_true_index == 0:
36 return 0
37
38 # Check both candidates and return the one with smaller difference
39 # Use <= to prefer smaller value in case of tie
40 if abs(calculate_sum(first_true_index - 1) - target) <= abs(calculate_sum(first_true_index) - target):
41 return first_true_index - 1
42 return first_true_index
431class Solution {
2 public int findBestValue(int[] arr, int target) {
3 Arrays.sort(arr);
4 int n = arr.length;
5
6 // Build prefix sum array
7 int[] prefixSum = new int[n + 1];
8 int maxValue = 0;
9 for (int i = 0; i < n; i++) {
10 prefixSum[i + 1] = prefixSum[i] + arr[i];
11 maxValue = Math.max(maxValue, arr[i]);
12 }
13
14 // Binary search template: find first value where sum >= target
15 int left = 0;
16 int right = maxValue;
17 int firstTrueIndex = -1;
18
19 while (left <= right) {
20 int mid = left + (right - left) / 2;
21 if (calculateSum(arr, prefixSum, mid) >= target) { // feasible condition
22 firstTrueIndex = mid;
23 right = mid - 1;
24 } else {
25 left = mid + 1;
26 }
27 }
28
29 // Edge case: no value produces sum >= target
30 if (firstTrueIndex == -1) {
31 return maxValue;
32 }
33
34 // Edge case: even value 0 produces sum >= target
35 if (firstTrueIndex == 0) {
36 return 0;
37 }
38
39 // Check both candidates
40 if (Math.abs(calculateSum(arr, prefixSum, firstTrueIndex - 1) - target)
41 <= Math.abs(calculateSum(arr, prefixSum, firstTrueIndex) - target)) {
42 return firstTrueIndex - 1;
43 }
44 return firstTrueIndex;
45 }
46
47 private int calculateSum(int[] arr, int[] prefixSum, int value) {
48 int index = upperBound(arr, value);
49 return prefixSum[index] + (arr.length - index) * value;
50 }
51
52 private int upperBound(int[] arr, int value) {
53 int left = 0;
54 int right = arr.length;
55 while (left < right) {
56 int mid = left + (right - left) / 2;
57 if (arr[mid] <= value) {
58 left = mid + 1;
59 } else {
60 right = mid;
61 }
62 }
63 return left;
64 }
65}
661class Solution {
2public:
3 int findBestValue(vector<int>& arr, int target) {
4 sort(arr.begin(), arr.end());
5 int n = arr.size();
6
7 // Build prefix sum array
8 vector<int> prefixSum(n + 1, 0);
9 int maxValue = 0;
10 for (int i = 0; i < n; ++i) {
11 prefixSum[i + 1] = prefixSum[i] + arr[i];
12 maxValue = max(maxValue, arr[i]);
13 }
14
15 auto calculateSum = [&](int value) {
16 int index = upper_bound(arr.begin(), arr.end(), value) - arr.begin();
17 return prefixSum[index] + (n - index) * value;
18 };
19
20 // Binary search template: find first value where sum >= target
21 int left = 0;
22 int right = maxValue;
23 int firstTrueIndex = -1;
24
25 while (left <= right) {
26 int mid = left + (right - left) / 2;
27 if (calculateSum(mid) >= target) { // feasible condition
28 firstTrueIndex = mid;
29 right = mid - 1;
30 } else {
31 left = mid + 1;
32 }
33 }
34
35 // Edge case: no value produces sum >= target
36 if (firstTrueIndex == -1) {
37 return maxValue;
38 }
39
40 // Edge case: even value 0 produces sum >= target
41 if (firstTrueIndex == 0) {
42 return 0;
43 }
44
45 // Check both candidates
46 if (abs(calculateSum(firstTrueIndex - 1) - target) <= abs(calculateSum(firstTrueIndex) - target)) {
47 return firstTrueIndex - 1;
48 }
49 return firstTrueIndex;
50 }
51};
521function findBestValue(arr: number[], target: number): number {
2 arr.sort((a, b) => a - b);
3 const n = arr.length;
4
5 // Build prefix sum array
6 const prefixSum: number[] = new Array(n + 1).fill(0);
7 let maxValue = 0;
8 for (let i = 0; i < n; i++) {
9 prefixSum[i + 1] = prefixSum[i] + arr[i];
10 maxValue = Math.max(maxValue, arr[i]);
11 }
12
13 const calculateSum = (value: number): number => {
14 const index = upperBound(arr, value);
15 return prefixSum[index] + (n - index) * value;
16 };
17
18 // Binary search template: find first value where sum >= target
19 let left = 0;
20 let right = maxValue;
21 let firstTrueIndex = -1;
22
23 while (left <= right) {
24 const mid = Math.floor((left + right) / 2);
25 if (calculateSum(mid) >= target) { // feasible condition
26 firstTrueIndex = mid;
27 right = mid - 1;
28 } else {
29 left = mid + 1;
30 }
31 }
32
33 // Edge case: no value produces sum >= target
34 if (firstTrueIndex === -1) {
35 return maxValue;
36 }
37
38 // Edge case: even value 0 produces sum >= target
39 if (firstTrueIndex === 0) {
40 return 0;
41 }
42
43 // Check both candidates
44 if (Math.abs(calculateSum(firstTrueIndex - 1) - target) <= Math.abs(calculateSum(firstTrueIndex) - target)) {
45 return firstTrueIndex - 1;
46 }
47 return firstTrueIndex;
48}
49
50function upperBound(arr: number[], value: number): number {
51 let left = 0;
52 let right = arr.length;
53 while (left < right) {
54 const mid = Math.floor((left + right) / 2);
55 if (arr[mid] <= value) {
56 left = mid + 1;
57 } else {
58 right = mid;
59 }
60 }
61 return left;
62}
63Solution Approach
We'll apply the binary search template to find the first value where sum >= target:
Step 1: Sort and prepare prefix sums
arr.sort()
prefix_sums = list(accumulate(arr, initial=0))
Sorting enables binary search lookups. Prefix sums let us calculate the sum for any value efficiently.
Step 2: Define helper function to calculate sum
def calculate_sum(value):
index = bisect_right(arr, value)
return prefix_sums[index] + (n - index) * value
For a given value:
- Find how many elements are ≤ value using bisect_right
- Sum = (unchanged elements) + (count of capped elements × value)
Step 3: Apply binary search template
left, right = 0, max(arr)
first_true_index = -1
while left <= right:
mid = (left + right) // 2
if calculate_sum(mid) >= target: # feasible condition
first_true_index = mid
right = mid - 1
else:
left = mid + 1
The template finds the first value where calculate_sum(value) >= target.
Step 4: Check both candidates
if first_true_index == -1:
return max(arr)
if first_true_index == 0:
return 0
if abs(calculate_sum(first_true_index - 1) - target) <= abs(calculate_sum(first_true_index) - target):
return first_true_index - 1
return first_true_index
The optimal answer is either first_true_index - 1 or first_true_index. We check both and return the one with smaller difference. The <= ensures we return the smaller value in case of ties.
Time Complexity: O(n log n) for sorting + O(log M × log n) for binary search where M = max(arr).
Space Complexity: O(n) for prefix sums 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 and build prefix sums
arr = [3, 4, 9]after sortingprefix_sums = [0, 3, 7, 16]
Step 2: Binary search using template
left = 0,right = 9(max of arr)first_true_index = -1
Iteration 1: mid = 4
calculate_sum(4): bisect_right([3,4,9], 4) = 2- Sum = prefix_sums[2] + (3-2)×4 = 7 + 4 = 11
- 11 >= 10? Yes (feasible)
first_true_index = 4,right = 3
Iteration 2: left = 0, right = 3, mid = 1
calculate_sum(1): bisect_right([3,4,9], 1) = 0- Sum = prefix_sums[0] + (3-0)×1 = 0 + 3 = 3
- 3 >= 10? No
left = 2
Iteration 3: left = 2, right = 3, mid = 2
calculate_sum(2): bisect_right([3,4,9], 2) = 0- Sum = prefix_sums[0] + (3-0)×2 = 0 + 6 = 6
- 6 >= 10? No
left = 3
Iteration 4: left = 3, right = 3, mid = 3
calculate_sum(3): bisect_right([3,4,9], 3) = 1- Sum = prefix_sums[1] + (3-1)×3 = 3 + 6 = 9
- 9 >= 10? No
left = 4
Loop ends: left = 4 > right = 3
first_true_index = 4(the first value where sum >= target)
Step 3: Check both candidates
calculate_sum(3) = 9, difference = |9 - 10| = 1calculate_sum(4) = 11, difference = |11 - 10| = 1- Since differences are equal (1 <= 1) and we prefer smaller value, return 3
Final Answer: 3
Time and Space Complexity
The time complexity is O(n log n + log M × log n) where n is the length of the array and M is the maximum value in the array.
Breaking down the operations:
arr.sort():O(n log n)for sorting the array- Building prefix sum array:
O(n) - Binary search on value space:
O(log M)iterations - Each iteration calls
calculate_sum()which usesbisect_right():O(log n) - Total for binary search:
O(log M × log n)
Overall time complexity: O(n log n + log M × log n). Since M is typically bounded by problem constraints or O(n), this simplifies to O(n log n).
The space complexity is O(n):
- Sorting may use
O(n)auxiliary space depending on the implementation - Prefix sum array requires
O(n)space - Other variables use
O(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: Using Wrong Template Variant
Problem: Using while left < right with right = mid instead of the template's while left <= right with right = mid - 1.
# Wrong: different template variant while left < right: mid = (left + right) // 2 if calculate_sum(mid) >= target: right = mid # doesn't track the answer else: left = mid + 1
Why it's wrong: This variant doesn't track first_true_index, making it harder to check both candidates. The template explicitly tracks the first feasible value.
Correct approach:
first_true_index = -1 while left <= right: mid = (left + right) // 2 if calculate_sum(mid) >= target: first_true_index = mid # track it right = mid - 1 else: left = mid + 1
Pitfall 2: Not Checking Both Candidates
Problem: After binary search, only returning first_true_index without comparing it to first_true_index - 1.
# Wrong: only returns first_true_index return first_true_index
Why it's wrong: The optimal value might be first_true_index - 1 if it produces an equal or closer difference to the target. We need to check both values around the boundary.
Correct approach:
if abs(calculate_sum(first_true_index - 1) - target) <= abs(calculate_sum(first_true_index) - target):
return first_true_index - 1
return first_true_index
Pitfall 3: Forgetting Tie-Breaking Rule
Problem: When two values produce the same difference, not returning the smaller value.
# Wrong: uses < instead of <=
if abs(sum_left - target) < abs(sum_right - target):
return first_true_index - 1
Why it's wrong: The problem asks for the smallest value in case of ties. Using < instead of <= returns the larger value when differences are equal.
Correct approach: Use <= to prefer the smaller value:
if abs(calculate_sum(first_true_index - 1) - target) <= abs(calculate_sum(first_true_index) - target):
return first_true_index - 1
Pitfall 4: Missing Edge Cases
Problem: Not handling cases where first_true_index is -1 or 0.
Why it's wrong: If all values produce sum < target, first_true_index stays -1. If even value 0 produces sum >= target, we can't check first_true_index - 1.
Correct approach:
if first_true_index == -1:
return max(arr) # no capping needed
if first_true_index == 0:
return 0 # even 0 is too large
In a binary min heap, the minimum element can be found in:
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
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!