1874. Minimize Product Sum of Two Arrays π
Problem Description
You are given two arrays nums1
and nums2
of equal length n
, containing positive integers.
The product sum of two equal-length arrays is calculated by multiplying corresponding elements at each position and then adding all these products together. Specifically, for arrays a
and b
, the product sum equals a[0] * b[0] + a[1] * b[1] + ... + a[n-1] * b[n-1]
.
For example, if a = [1,2,3,4]
and b = [5,2,3,1]
, the product sum would be 1*5 + 2*2 + 3*3 + 4*1 = 22
.
Your task is to find the minimum possible product sum by rearranging the elements in nums1
(you can change the order of elements in nums1
however you like, but nums2
remains fixed).
The key insight is that to minimize the product sum, you should pair the largest values in one array with the smallest values in the other array. This is achieved by sorting nums1
in ascending order and nums2
in descending order, then calculating the product sum of these rearranged arrays. This greedy approach ensures that large numbers are multiplied by small numbers, minimizing the overall sum.
Intuition
To minimize the product sum, we need to think about how multiplication works. When we multiply two numbers, the result grows larger when both numbers are large, and stays smaller when at least one of them is small.
Consider a simple example: if we have nums1 = [1, 4]
and nums2 = [2, 3]
. We can rearrange nums1
to get two possible product sums:
- If
nums1 = [1, 4]
: product sum =1*2 + 4*3 = 2 + 12 = 14
- If
nums1 = [4, 1]
: product sum =4*2 + 1*3 = 8 + 3 = 11
Notice that the second arrangement gives us a smaller sum. Why? Because we paired the largest number in nums1
(which is 4) with the smallest number in nums2
(which is 2), and the smallest in nums1
(which is 1) with the largest in nums2
(which is 3).
This reveals the key pattern: to minimize the sum of products, we should pair large numbers with small numbers. Think of it as "balancing" - we want to avoid situations where two large numbers multiply together (which would create a very large product).
The greedy strategy emerges naturally: sort one array in ascending order and the other in descending order. This ensures that:
- The largest element in one array gets multiplied by the smallest element in the other
- The second-largest gets multiplied by the second-smallest
- And so on...
This pairing strategy minimizes each individual product term, which in turn minimizes the total sum. Since we can only rearrange nums1
, we sort it in ascending order and sort nums2
in descending order (or vice versa - the result would be the same).
Solution Approach
The implementation follows a straightforward greedy approach with sorting:
-
Sort nums1 in ascending order: We use
nums1.sort()
to arrange the elements from smallest to largest. This ensures that the smallest elements ofnums1
will be at the beginning of the array. -
Sort nums2 in descending order: We use
nums2.sort(reverse=True)
to arrange the elements from largest to smallest. This ensures that the largest elements ofnums2
will be at the beginning of the array. -
Calculate the product sum: After sorting, we pair up the elements at corresponding positions. The smallest element in
nums1
gets multiplied with the largest element innums2
, the second-smallest innums1
with the second-largest innums2
, and so on.
The calculation is done efficiently using Python's zip()
function combined with a generator expression:
zip(nums1, nums2)
creates pairs of elements at the same index from both arraysx * y for x, y in zip(nums1, nums2)
generates the product of each pairsum()
adds up all these products to get the final result
For example, if after sorting we have:
nums1 = [1, 2, 3, 4]
(ascending)nums2 = [5, 3, 2, 1]
(descending)
The product sum would be: 1*5 + 2*3 + 3*2 + 4*1 = 5 + 6 + 6 + 4 = 21
The time complexity is O(n log n)
due to the sorting operations, where n
is the length of the arrays. The space complexity is O(1)
if we consider the sorting to be in-place (though technically Python's sort uses O(n)
space).
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 walk through a concrete example to see how the solution works.
Given:
nums1 = [3, 5, 1]
nums2 = [4, 6, 2]
Goal: Find the minimum product sum by rearranging nums1
.
Step 1: Sort nums1 in ascending order
nums1.sort() β nums1 = [1, 3, 5]
We arrange nums1 from smallest to largest.
Step 2: Sort nums2 in descending order
nums2.sort(reverse=True) β nums2 = [6, 4, 2]
We arrange nums2 from largest to smallest.
Step 3: Calculate the product sum Now we pair up elements at corresponding positions:
- Position 0:
1 Γ 6 = 6
(smallest in nums1 Γ largest in nums2) - Position 1:
3 Γ 4 = 12
(middle in nums1 Γ middle in nums2) - Position 2:
5 Γ 2 = 10
(largest in nums1 Γ smallest in nums2)
Product sum = 6 + 12 + 10 = 28
Why is this minimal?
Let's verify by checking another arrangement. If we had kept nums1 as [3, 5, 1]
:
3 Γ 4 = 12
5 Γ 6 = 30
1 Γ 2 = 2
- Sum = 12 + 30 + 2 = 44
Notice that 44 > 28. The greedy approach of pairing large with small values prevents situations like 5 Γ 6 = 30
where two large numbers multiply to create an even larger product. By systematically pairing opposites (largest with smallest), we minimize each individual product term, resulting in the minimum possible sum.
Solution Implementation
1class Solution:
2 def minProductSum(self, nums1: List[int], nums2: List[int]) -> int:
3 """
4 Calculate the minimum product sum of two arrays.
5
6 Strategy: Sort one array in ascending order and the other in descending order,
7 then pair elements at the same indices. This ensures small values are multiplied
8 with large values, minimizing the overall sum.
9
10 Args:
11 nums1: First list of integers
12 nums2: Second list of integers (same length as nums1)
13
14 Returns:
15 The minimum possible sum of products after rearranging elements
16 """
17 # Sort first array in ascending order (smallest to largest)
18 nums1.sort()
19
20 # Sort second array in descending order (largest to smallest)
21 nums2.sort(reverse=True)
22
23 # Calculate sum of products by pairing elements at corresponding positions
24 # zip() pairs elements: (nums1[0], nums2[0]), (nums1[1], nums2[1]), ...
25 # This pairing minimizes the sum since small values multiply with large values
26 min_product_sum = sum(x * y for x, y in zip(nums1, nums2))
27
28 return min_product_sum
29
1class Solution {
2 /**
3 * Calculates the minimum product sum of two arrays.
4 * Strategy: Sort both arrays and pair smallest elements from one array
5 * with largest elements from the other array to minimize the product sum.
6 *
7 * @param nums1 First integer array
8 * @param nums2 Second integer array of same length as nums1
9 * @return Minimum possible sum of products after rearranging elements
10 */
11 public int minProductSum(int[] nums1, int[] nums2) {
12 // Sort both arrays in ascending order
13 Arrays.sort(nums1);
14 Arrays.sort(nums2);
15
16 // Get the length of arrays (both arrays have same length)
17 int arrayLength = nums1.length;
18
19 // Initialize sum accumulator
20 int minimumSum = 0;
21
22 // Pair elements: smallest from nums1 with largest from nums2
23 for (int i = 0; i < arrayLength; ++i) {
24 // Multiply element at index i from nums1 with element at
25 // corresponding position from end of nums2
26 minimumSum += nums1[i] * nums2[arrayLength - i - 1];
27 }
28
29 return minimumSum;
30 }
31}
32
1class Solution {
2public:
3 int minProductSum(vector<int>& nums1, vector<int>& nums2) {
4 // Sort first array in ascending order
5 sort(nums1.begin(), nums1.end());
6
7 // Sort second array in descending order
8 sort(nums2.begin(), nums2.end(), greater<int>());
9
10 // Get the size of arrays (both arrays have same size)
11 int n = nums1.size();
12
13 // Initialize the sum of products
14 int result = 0;
15
16 // Calculate the sum of products by pairing smallest from nums1
17 // with largest from nums2, and so on
18 for (int i = 0; i < n; ++i) {
19 result += nums1[i] * nums2[i];
20 }
21
22 // Return the minimum product sum
23 return result;
24 }
25};
26
1/**
2 * Calculates the minimum product sum of two arrays by pairing elements optimally.
3 * Strategy: Sort one array ascending and the other descending, then pair elements at same indices.
4 * This ensures smallest elements are multiplied with largest elements, minimizing the sum.
5 *
6 * @param nums1 - First array of numbers
7 * @param nums2 - Second array of numbers (same length as nums1)
8 * @returns The minimum possible sum of products when pairing elements from both arrays
9 */
10function minProductSum(nums1: number[], nums2: number[]): number {
11 // Sort first array in ascending order (smallest to largest)
12 nums1.sort((a: number, b: number) => a - b);
13
14 // Sort second array in descending order (largest to smallest)
15 nums2.sort((a: number, b: number) => b - a);
16
17 // Initialize sum accumulator
18 let sum: number = 0;
19
20 // Calculate sum of products by pairing elements at corresponding indices
21 for (let i: number = 0; i < nums1.length; ++i) {
22 sum += nums1[i] * nums2[i];
23 }
24
25 return sum;
26}
27
Time and Space Complexity
Time Complexity: O(n Γ log n)
, where n
is the length of the arrays nums1
and nums2
.
The time complexity is dominated by the sorting operations:
nums1.sort()
takesO(n Γ log n)
timenums2.sort(reverse=True)
takesO(n Γ log n)
time- The
zip
andsum
operations with generator expression takeO(n)
time
Overall: O(n Γ log n) + O(n Γ log n) + O(n) = O(n Γ log n)
Space Complexity: O(log n)
, where n
is the length of the arrays.
The space complexity comes from:
- Python's Timsort algorithm (used in
.sort()
) requiresO(log n)
space for the recursion stack in the worst case - Both sorting operations are done in-place, so no additional
O(n)
space is needed for the arrays themselves - The generator expression
(x * y for x, y in zip(nums1, nums2))
usesO(1)
space as it generates values on-the-fly
Overall: O(log n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying Input Arrays Without Consideration
The solution sorts both nums1
and nums2
in-place, which permanently modifies the original arrays. This can be problematic if:
- The original array order needs to be preserved for other operations
- The function is called multiple times with the same array references
- The caller expects the arrays to remain unchanged
Example of the issue:
nums1 = [3, 5, 2, 1] nums2 = [4, 2, 1, 3] result = minProductSum(nums1, nums2) # After the function call: # nums1 is now [1, 2, 3, 5] - permanently changed! # nums2 is now [4, 3, 2, 1] - permanently changed!
Solution: Create copies of the arrays before sorting:
def minProductSum(self, nums1: List[int], nums2: List[int]) -> int:
# Create copies to avoid modifying original arrays
sorted_nums1 = sorted(nums1) # Creates a new sorted list
sorted_nums2 = sorted(nums2, reverse=True) # Creates a new sorted list
min_product_sum = sum(x * y for x, y in zip(sorted_nums1, sorted_nums2))
return min_product_sum
2. Integer Overflow in Other Languages
While Python handles arbitrarily large integers automatically, implementing this solution in languages like C++ or Java requires careful consideration of integer overflow when dealing with large numbers.
Example scenario:
- If
nums1[i]
andnums2[i]
are both near10^5
and array length is10^5
- Each product could be up to
10^10
- The sum could exceed the range of a 32-bit integer
Solution for other languages: Use appropriate data types:
// C++ example
long long minProductSum(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end(), greater<int>());
long long result = 0; // Use long long instead of int
for (int i = 0; i < nums1.size(); i++) {
result += (long long)nums1[i] * nums2[i];
}
return result;
}
3. Assuming Both Arrays Can Be Rearranged
The problem specifically states that only nums1
can be rearranged while nums2
remains fixed. A common mistake is sorting both arrays thinking both can be rearranged freely.
Incorrect interpretation:
# WRONG: This assumes both arrays can be rearranged
nums1.sort()
nums2.sort() # Should not sort nums2 in ascending order
min_sum = sum(x * y for x, y in zip(nums1, nums2))
Correct approach when only nums1 can be rearranged:
def minProductSum(self, nums1: List[int], nums2: List[int]) -> int:
# Only sort nums1 based on nums2's values
# Pair largest in nums2 with smallest in nums1
nums2_indexed = [(val, idx) for idx, val in enumerate(nums2)]
nums2_indexed.sort(reverse=True) # Sort by value descending
nums1.sort() # Sort nums1 ascending
result = 0
for i, (val2, original_idx) in enumerate(nums2_indexed):
result += nums1[i] * val2
return result
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
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!