Taro Logo

Minimum Cost to Make Array Equal

Hard
Asked by:
Profile picture
Profile picture
Profile picture
Profile picture
10 views
Topics:
ArraysGreedy Algorithms

You are given two 0-indexed arrays nums and cost consisting each of n positive integers.

You can do the following operation any number of times:

  • Increase or decrease any element of the array nums by 1.

The cost of doing one operation on the ith element is cost[i].

Return the minimum total cost such that all the elements of the array nums become equal.

Example 1:

Input: nums = [1,3,5,2], cost = [2,3,1,14]
Output: 8
Explanation: We can make all the elements equal to 2 in the following way:
- Increase the 0th element one time. The cost is 2.
- Decrease the 1st element one time. The cost is 3.
- Decrease the 2nd element three times. The cost is 1 + 1 + 1 = 3.
The total cost is 2 + 3 + 3 = 8.
It can be shown that we cannot make the array equal with a smaller cost.

Example 2:

Input: nums = [2,2,2,2,2], cost = [4,2,8,1,3]
Output: 0
Explanation: All the elements are already equal, so no operations are needed.

Constraints:

  • n == nums.length == cost.length
  • 1 <= n <= 105
  • 1 <= nums[i], cost[i] <= 106
  • Test cases are generated in a way that the output doesn't exceed 253-1

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What is the range of values within the input array? Are negative numbers, zeros, or floating-point numbers possible?
  2. What is the maximum size of the input array?
  3. Are there any constraints on the cost to increment or decrement each element, or can I assume a cost of 1 per increment/decrement?
  4. If the array is empty or contains only one element, what should the function return?
  5. If there are multiple target values that result in the minimum cost, can I return the cost for any of them?

Brute Force Solution

Approach

To find the cheapest way to make all numbers in a list equal, we'll try making them equal to every possible number. We'll calculate the total cost for each possibility and then pick the one with the lowest cost.

Here's how the algorithm would work step-by-step:

  1. Consider each number as a potential target value that all the other numbers should be changed to.
  2. For each target value, find the difference between it and every other number in the list.
  3. Multiply each of those differences by their corresponding cost.
  4. Add up all those multiplied differences to find the total cost for using that specific target value.
  5. Repeat the above steps for every possible target value.
  6. After calculating the total cost for every target value, compare all the costs.
  7. Select the target value that resulted in the lowest total cost. This is the minimum cost to make the list equal.

Code Implementation

def min_cost_to_make_array_equal_brute_force(numbers, costs):
    minimum_total_cost = float('inf')

    # Iterate through each number as a potential target value
    for target_value in numbers:

        current_total_cost = 0

        # Calculate the cost to convert all numbers to target_value
        for index in range(len(numbers)):
            difference = abs(numbers[index] - target_value)
            current_total_cost += difference * costs[index]

        # Update min cost if the current cost is lower
        minimum_total_cost = min(minimum_total_cost, current_total_cost)

    return minimum_total_cost

Big(O) Analysis

Time Complexity
O(n²)The algorithm iterates through each number in the input array of size n, considering it as a potential target value. For each target value, the algorithm iterates through the remaining n-1 numbers in the array to calculate the cost of changing each number to the target value. Therefore, the algorithm performs approximately n * (n-1) operations. This simplifies to n² - n, and in Big O notation, the dominant term is n². Hence, the time complexity is O(n²).
Space Complexity
O(1)The provided plain English explanation calculates costs iteratively without using any auxiliary data structures that scale with the input size. It considers each number as a target and calculates costs directly. The total cost for each target is tracked in a single variable. Therefore, the auxiliary space used is constant, independent of the number of elements in the list (N).

Optimal Solution

Approach

The goal is to find a target value that, if every element in the array was changed to, would give us the minimum overall cost. Instead of testing every single possible target value, we cleverly look for a specific value that is guaranteed to be the best or close to the best.

Here's how the algorithm would work step-by-step:

  1. First, organize all the numbers based on how much it costs to change each one.
  2. Realize that the absolute best target value to convert all array elements to will occur at one of the original array's values. We do not need to consider values outside of the original array, because the cost will be minimized at one of the existing values.
  3. Find the number that divides the cost in half. This means about half the elements are cheaper to change to this number, and the other half are more expensive.
  4. Check the costs if we converted all values to the numbers just before and after our 'middle' number, to see which one results in an even lower total cost.
  5. The number (or one of its close neighbors) we identified is the optimal target value. Calculate the total cost of changing all numbers to this target value.

Code Implementation

def min_cost_to_make_array_equal(numbers, costs):
    number_cost_pairs = sorted(zip(numbers, costs))
    numbers_sorted = [number for number, _ in number_cost_pairs]
    costs_sorted = [cost for _, cost in number_cost_pairs]

    total_cost_so_far = 0
    total_costs_sum = sum(costs_sorted)

    # Find the median by iterating until half of cost is reached
    for i, cost in enumerate(costs_sorted):
        total_cost_so_far += cost
        if total_cost_so_far >= total_costs_sum / 2:
            median_index = i
            break

    median_number = numbers_sorted[median_index]

    # Target could be one element before or after the median.
    possible_targets = set()
    possible_targets.add(median_number)
    if median_index > 0:
        possible_targets.add(numbers_sorted[median_index - 1])
    if median_index < len(numbers) - 1:
        possible_targets.add(numbers_sorted[median_index + 1])

    minimum_cost = float('inf')

    # Check the cost for each of the possible target values.
    for target_value in possible_targets:
        current_cost = 0
        for i, number in enumerate(numbers):
            current_cost += abs(number - target_value) * costs[i]
        minimum_cost = min(minimum_cost, current_cost)

    return minimum_cost

Big(O) Analysis

Time Complexity
O(n log n)The algorithm first sorts the input array of size n based on cost, which takes O(n log n) time. Then, it iterates through the sorted array once to find the median element based on cumulative cost, which takes O(n) time. Finally, it calculates the total cost for at most three potential target values (the median and its neighbors), each calculation taking O(n) time. Since sorting dominates the other operations, the overall time complexity is O(n log n).
Space Complexity
O(N)The algorithm first organizes the numbers based on cost, implying a sorting operation which may use auxiliary space depending on the sorting algorithm used. In the worst case (e.g., merge sort), sorting N numbers requires O(N) auxiliary space. The algorithm also stores the cost for numbers before and after the 'middle' number, which involves iterating the array, however the storage for this operation is constant. Therefore, the dominant space complexity is due to the sorting, leading to O(N) space.

Edge Cases

CaseHow to Handle
Empty or null input arrayReturn 0 immediately as there's no cost to make an empty array equal.
Array with a single elementReturn 0 immediately, as a single-element array is already equal.
Large array size potentially causing integer overflow during cost calculationUse 64-bit integers (long in Java/C++) to store intermediate cost values to prevent overflow.
Array with all elements identicalReturn 0 immediately as the array is already equal.
Array with negative numbers in values or costsEnsure the cost function handles negative values correctly or, if the problem statement prohibits them, return an error.
Array with very large numerical values for elements or costsCheck for potential integer overflow issues during calculations; consider using larger data types or modular arithmetic if applicable and problem constraints allow it.
Array where the optimal value to equalize to is outside the range of input valuesConsider all numbers in the input array as potential target values, or explicitly search for the optimal value within the bounds of possible input values.
Cases where integer costs cause an overflow during addition, when calculating the cost of each valueUse long type or an arbitrary-precision arithmetic library to avoid integer overflow during cost calculation.