Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1300. Sum of Mutated Array Closest to Target - Leetcode Solution

Problem Description

You are given an integer array arr and an integer target. You can "mutate" the array by replacing every element in arr that is greater than or equal to some integer value value with value itself. Your goal is to choose a value such that the sum of the mutated array is as close as possible to target. If there are multiple answers, return the smallest such value.

  • Each element in arr can be replaced at most once, and only if it's greater than or equal to value.
  • All elements less than value remain unchanged.
  • There is always at least one valid solution.
  • You must not reuse elements; each is mutated independently.

Example:
arr = [2,3,5], target = 10
If you choose value = 5, the array remains the same and the sum is 10.

Thought Process

At first glance, one might consider trying every possible value from 1 up to the maximum element in arr, mutate the array each time, and check which sum is closest to target. However, this brute-force approach is inefficient, especially for large arrays or large values.

We can observe that increasing value will never increase the sum of the mutated array, because more elements are being capped at a lower number. This monotonicity suggests that binary search could be effective. Instead of checking all possible value values, we can efficiently "zoom in" on the optimal one.

The key insight is to realize that for a given value, the mutated sum can be computed quickly, and that the function mapping value to mutated sum is non-increasing. So, we can use binary search to find the smallest value that gets us closest to target.

Solution Approach

  • Step 1: Understand the Mutation Function
    For a given value, mutate each element in arr by replacing it with value if it is greater than value. The mutated sum is the sum of all mutated elements.
  • Step 2: Binary Search for Optimal Value
    Since the mutated sum decreases as value decreases, we can use binary search on value in the range [1, max(arr)] to find the value that makes the mutated sum closest to target.
  • Step 3: Compute the Mutated Sum Efficiently
    For a given value, iterate through arr, sum each element as-is if it is less than value, or add value otherwise.
  • Step 4: Track the Closest Value
    During binary search, keep track of the value that produces the sum with the smallest absolute difference to target. If there is a tie, prefer the smaller value.
  • Step 5: Return the Result
    After binary search completes, return the value that yields the closest mutated sum.

This approach is much more efficient than brute force, as it reduces the search space from potentially thousands of values to at most log(max(arr)) iterations.

Example Walkthrough

Let's use arr = [2, 3, 5] and target = 10.

  • Initial Range: value can be from 1 to 5.
  • value = 3: Mutated array: [2, 3, 3]. Sum = 8.
    Difference to target: 2.
  • value = 4: Mutated array: [2, 3, 4]. Sum = 9.
    Difference to target: 1.
  • value = 5: Mutated array: [2, 3, 5]. Sum = 10.
    Difference to target: 0.
  • Result: The closest sum is achieved when value = 5.

The binary search will efficiently narrow down to value = 5 as the answer.

Time and Space Complexity

  • Brute-force approach: For each possible value from 1 to max(arr), compute the mutated sum (O(n) per value). This leads to O(n * max(arr)) time.
  • Optimized (Binary Search) approach: Binary search over possible value (O(log(max(arr)))) and for each, compute the mutated sum (O(n)). So overall O(n log(max(arr))) time.
  • Space Complexity: O(1) extra space (not counting input), since we only use a few variables for tracking sums and indices.

Summary

By recognizing the monotonic relationship between the mutation value and the mutated sum, we can use binary search to efficiently find the best value that brings the sum closest to the target. The approach is both elegant and performant, reducing a potentially expensive brute-force search to a fast, logarithmic one. This problem is a great example of how understanding the properties of a function (in this case, monotonicity) can lead to a dramatic optimization.

Code Implementation

class Solution:
    def findBestValue(self, arr, target):
        def mutated_sum(value):
            return sum(min(x, value) for x in arr)
        
        left, right = 0, max(arr)
        closest, answer = float('inf'), 0
        
        while left <= right:
            mid = (left + right) // 2
            curr_sum = mutated_sum(mid)
            diff = abs(curr_sum - target)
            if diff < closest or (diff == closest and mid < answer):
                closest = diff
                answer = mid
            if curr_sum < target:
                left = mid + 1
            else:
                right = mid - 1
        return answer
      
class Solution {
public:
    int findBestValue(vector<int>& arr, int target) {
        auto mutated_sum = [&](int value) {
            int sum = 0;
            for (int x : arr) sum += std::min(x, value);
            return sum;
        };
        int left = 0, right = *max_element(arr.begin(), arr.end());
        int closest = INT_MAX, answer = 0;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            int curr_sum = mutated_sum(mid);
            int diff = abs(curr_sum - target);
            if (diff < closest || (diff == closest && mid < answer)) {
                closest = diff;
                answer = mid;
            }
            if (curr_sum < target)
                left = mid + 1;
            else
                right = mid - 1;
        }
        return answer;
    }
};
      
class Solution {
    public int findBestValue(int[] arr, int target) {
        int left = 0, right = 0;
        for (int x : arr) right = Math.max(right, x);
        int closest = Integer.MAX_VALUE, answer = 0;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            int currSum = 0;
            for (int x : arr) currSum += Math.min(x, mid);
            int diff = Math.abs(currSum - target);
            if (diff < closest || (diff == closest && mid < answer)) {
                closest = diff;
                answer = mid;
            }
            if (currSum < target)
                left = mid + 1;
            else
                right = mid - 1;
        }
        return answer;
    }
}
      
var findBestValue = function(arr, target) {
    const mutatedSum = value => arr.reduce((sum, x) => sum + Math.min(x, value), 0);
    let left = 0, right = Math.max(...arr);
    let closest = Infinity, answer = 0;
    while (left <= right) {
        let mid = Math.floor((left + right) / 2);
        let currSum = mutatedSum(mid);
        let diff = Math.abs(currSum - target);
        if (diff < closest || (diff === closest && mid < answer)) {
            closest = diff;
            answer = mid;
        }
        if (currSum < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return answer;
};