Facebook Pixel

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 than value gets replaced with value
  • 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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
43
1class 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}
66
1class 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};
52
1function 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}
63

Solution 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 Evaluator

Example 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 sorting
  • prefix_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| = 1
  • calculate_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 uses bisect_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
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the minimum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More