2561. Rearranging Fruits
Problem Description
In this problem, we are given two fruit baskets with exactly n
fruits each. These fruit baskets are represented by two integer arrays basket1
and basket2
where each element indicates the cost of the respective fruit. Our task is to make both baskets contain the exact same fruits in terms of their costs, which means that if we sort both baskets by their fruits' costs, they should look identical.
However, there are rules on how we can make the two baskets equal:
- We can choose to swap the
i-th
fruit frombasket1
with thej-th
fruit frombasket2
. - The cost incurred for swapping these two fruits is
min(basket1[i], basket2[j])
.
The goal is to find the minimum total cost required to make both baskets equal through such swapping operations. If it's not possible to make both baskets equal, we should return -1
.
Intuition
To solve this problem, we need to follow a series of logical steps:
-
Frequency Counting: First, we use a frequency counter to understand the difference between the baskets in terms of fruit costs. For each matching pair of fruits (same cost in both baskets), there's no action required. If a fruit cost in
basket1
is not counter-balanced by the same cost inbasket2
, then alternations are needed. -
Balancing Counts: We need to check if each fruit's imbalanced count is even. An odd imbalance means we cannot fully swap fruits to balance the baskets, so we return
-1
. For instance, an extra three fruits of cost 5 inbasket1
cannot be balanced by swaps since we would always end up with an uncoupled fruit. -
Calculating Minimum Swap Cost: Since only pairs of unbalanced fruits need swapping, we sort the unbalanced fruits by their costs. We can then calculate the cost to swap half of these unbalanced fruits (each swap involves two fruits, hence 'half'). We choose either the cost of the current fruit or two times the minimum fruit cost for each swap, whichever is lesser, to ensure the lowest possible swap cost.
-
Summation of Costs: Summing up the swap costs will result in the minimum cost required to balance the baskets.
By following the code provided according to the intuition above, we ensure that we're swapping fruits in the most cost-effective way possible, resulting in the minimum cost to make both baskets equal.
Learn more about Greedy patterns.
Solution Approach
Step-by-Step Implementation:
-
Create a Counter Object: Using the
Counter
class from thecollections
module we create acnt
object to track the balance of fruits betweenbasket1
andbasket2
. A positive value incnt
means there are more fruits of that cost inbasket1
and vice versa. -
Zip and Update Counts: We iterate through
basket1
andbasket2
in tandem usingzip
:for a, b in zip(basket1, basket2): cnt[a] += 1 cnt[b] -= 1
For each pair
(a, b)
of fruits, we increment the count of fruita
incnt
because it's frombasket1
, and decrement for fruitb
because it's frombasket2
. -
Find the Minimum Cost Fruit: We find the minimum cost fruit that can be used for calculating the swap cost using
min(cnt)
. -
Prepare List of Imbalanced Fruits: Iterate over the
cnt
to find the imbalances. If there's an odd imbalance, we cannot make a swap to balance the fruit, and therefore the task is impossible and we return-1
:nums = [] for x, v in cnt.items(): if v % 2: return -1 nums.extend([x] * (abs(v) // 2))
We repeat the fruit cost value
abs(v) // 2
times because we need pairs for swapping. -
Sort the List: Sorting the list of costs ensures that when we choose fruits to swap, we always consider the least costly.
nums.sort()
-
Calculate the Cost for Half the Swaps: We only need to make swaps for half of the imbalanced fruits. For each imbalanced fruit cost, choose the lower of the fruit's cost itself or double the minimum cost fruit:
m = len(nums) // 2 total_cost = sum(min(x, mi * 2) for x in nums[:m])
-
Return the Minimum Total Cost: Finally, we return the sum which represents the minimum cost to make both the baskets contain the same fruits.
By using a Counter
to maintain the frequency of the fruits' costs, determining the least cost fruit for swap calculations, and leveraging sorting to guide our swapping strategy, we establish an efficient approach to determine the minimum swapping cost required—or recognize that balancing the baskets is infeasible.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach with two fruit baskets basket1
and basket2
, each with n=5
fruits:
basket1 = [1, 3, 3, 2, 5] basket2 = [2, 1, 4, 3, 4]
We want to find the minimum total cost to make the fruits in both baskets have the same costs after swapping.
-
Create a Counter Object: Create a counter to track the balance of fruits:
cnt = Counter()
-
Zip and Update Counts: We compare
basket1
andbasket2
simultaneously and update our counter:cnt[1] += 1 # From basket1 cnt[2] -= 1 # From basket2 (counterpart for basket1[1]) cnt[3] += 1 # From basket1 cnt[1] -= 1 # From basket2 (counterpart for basket1[3]) cnt[3] += 1 # From basket1 cnt[4] -= 1 # From basket2 (counterpart for basket1[3]) cnt[2] += 1 # From basket1 cnt[3] -= 1 # From basket2 (counterpart for basket1[2]) cnt[5] += 1 # From basket1 cnt[4] -= 1 # From basket2 (counterpart for basket1[5]) After updating counts, `cnt` looks like this: cnt = {1: 0, 3: 1, 2: 0, 4: -2, 5: 1}
-
Find the Minimum Cost Fruit: The minimum fruit cost from the imbalanced fruits (
cnt
) is 3. -
Prepare List of Imbalanced Fruits: Check the imbalances and gather the fruits that need to be swapped:
nums = [3, 5] # We need to swap one fruit with cost 3 and one with cost 5
Since there are no odd values in
cnt
, we proceed. -
Sort the List: We sort the imbalances to ensure we consider the least costly fruits first:
nums.sort() nums = [3, 5]
-
Calculate the Cost for Half the Swaps: There are two imbalanced fruits, so we need one swap:
mi = 3 # minimum cost from imbalanced fruits m = 1 # number of swaps needed total_cost = min(3, 3 * 2) for the first fruit
In this case, we choose the fruit's own cost, which is 3, since it's not greater than double the minimum cost fruit.
-
Return the Minimum Total Cost: The sum is the minimum cost for making both the baskets equal:
total_cost = 3
The minimum total cost required to make both baskets contain the same fruits in terms of cost is thus 3
. This would involve swapping one of the fruits with cost 3
from basket1
with one of the fruits with cost 4
from basket2
.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def minCost(self, basket1: List[int], basket2: List[int]) -> int:
5 # Create a counter to track the frequency of each fruit type
6 fruit_counter = Counter()
7
8 # Iterate over both baskets simultaneously
9 for fruit_type_a, fruit_type_b in zip(basket1, basket2):
10 # Increment the count for fruit type from basket1
11 fruit_counter[fruit_type_a] += 1
12 # Decrement the count for fruit type from basket2
13 fruit_counter[fruit_type_b] -= 1
14
15 # Get the minimum fruit type value from our counter
16 min_fruit_type = min(fruit_counter)
17
18 # Prepare a list to count how many exchanges are needed
19 exchange_list = []
20
21 # Check if exchange is possible given the counts
22 for fruit_type, count in fruit_counter.items():
23 if count % 2:
24 # If count is odd, return -1 (impossible to exchange)
25 return -1
26 # Add the fruit types for exchange to the list
27 exchange_list.extend([fruit_type] * (abs(count) // 2))
28
29 # Sort the list to facilitate minimum cost calculation
30 exchange_list.sort()
31
32 # Find the middle point of our sorted list
33 mid_point = len(exchange_list) // 2
34
35 # Calculate and return the cost of exchanges
36 # By taking minimum exchange cost between the fruit type and double the minimum fruit type
37 return sum(min(fruit_type, min_fruit_type * 2) for fruit_type in exchange_list[:mid_point])
38
1class Solution {
2 public long minCost(int[] basket1, int[] basket2) {
3 int n = basket1.length; // Length of the baskets
4 Map<Integer, Integer> fruitCountMap = new HashMap<>(); // A map to store the count difference of fruits between baskets
5
6 // Count the difference between the two baskets
7 for (int i = 0; i < n; ++i) {
8 fruitCountMap.merge(basket1[i], 1, Integer::sum); // Increment count for the current fruit in basket1
9 fruitCountMap.merge(basket2[i], -1, Integer::sum); // Decrement count for the current fruit in basket2
10 }
11
12 int minFruitValue = Integer.MAX_VALUE; // Initialize the minimum fruit value
13 List<Integer> fruitDifferences = new ArrayList<>(); // List to store absolute differences
14
15 // Analyze the map to find out the absolute difference and minimum fruit value
16 for (var entry : fruitCountMap.entrySet()) {
17 int fruit = entry.getKey(), count = entry.getValue();
18 if (count % 2 != 0) { // If count is odd, there's no way to balance, return -1
19 return -1;
20 }
21 for (int i = Math.abs(count) / 2; i > 0; --i) {
22 fruitDifferences.add(fruit); // Add the fruit differences
23 }
24 minFruitValue = Math.min(minFruitValue, fruit); // Update the minimum fruit value if necessary
25 }
26
27 Collections.sort(fruitDifferences); // Sort the list of differences
28
29 int m = fruitDifferences.size(); // Size of the list of differences
30 long totalCost = 0; // Initialize the total cost
31
32 // Calculate the minimum cost of balancing the baskets
33 for (int i = 0; i < m / 2; ++i) {
34 totalCost += Math.min(fruitDifferences.get(i), minFruitValue * 2); // Take the minimum of the current fruit difference and double of min fruit value
35 }
36
37 return totalCost; // Return the total minimum cost
38 }
39}
40
1class Solution {
2public:
3 // This method calculates the minimum cost to make two baskets identical
4 long long minCost(vector<int>& basket1, vector<int>& basket2) {
5 int n = basket1.size();
6 unordered_map<int, int> countMap; // Map to store the difference in counts of fruits
7
8 // Calculate the count differences for each fruit
9 for (int i = 0; i < n; ++i) {
10 countMap[basket1[i]]++;
11 countMap[basket2[i]]--;
12 }
13
14 int minFruit = INT_MAX; // To store the minimum fruit value encountered
15 vector<int> disparities; // Vector to store fruits which have disparities
16
17 // Loop through the countMap to find disparities
18 for (auto& [fruit, count] : countMap) {
19 // If the count is odd, we cannot make the baskets identical so return -1
20 if (count % 2) {
21 return -1;
22 }
23
24 // Add the absolute half of the count of each fruit to the disparities vector
25 for (int i = abs(count) / 2; i > 0; --i) {
26 disparities.push_back(fruit);
27 }
28
29 // Update the minimum fruit value
30 minFruit = min(minFruit, fruit);
31 }
32
33 // Sort the disparities vector in ascending order
34 sort(disparities.begin(), disparities.end());
35
36 int m = disparities.size(); // Size of the disparities vector
37 long long totalCost = 0; // To store the total cost required to make the baskets identical
38
39 // Loop to calculate the minimum cost required
40 for (int i = 0; i < m / 2; ++i) {
41 // Cost is the minimum between swapping with the cheapest fruit twice or the current disparity
42 totalCost += min(disparities[i], minFruit * 2);
43 }
44
45 // Return the total cost calculated
46 return totalCost;
47 }
48};
49
1// Importing required collections
2import { HashMap } from "tstl/container/HashMap";
3import { min } from "tstl/algorithm/math";
4
5// This method calculates the minimum cost to make two baskets identical
6function minCost(basket1: number[], basket2: number[]): number {
7 const n: number = basket1.length;
8 const countMap: HashMap<number, number> = new HashMap(); // Map to store the difference in counts of fruits
9
10 // Calculate the count differences for each fruit
11 for (let i = 0; i < n; ++i) {
12 // Increase the count for the current fruit in basket1
13 countMap.set(basket1[i], (countMap.get(basket1[i]) || 0) + 1);
14 // Decrease the count for the current fruit in basket2
15 countMap.set(basket2[i], (countMap.get(basket2[i]) || 0) - 1);
16 }
17
18 let minFruit: number = Number.MAX_VALUE; // To store the minimum fruit value encountered
19 const disparities: number[] = []; // Array to store fruits which have disparities
20
21 // Loop through the countMap to find disparities
22 countMap.forEach((count, fruit) => {
23 // If the count is odd, we cannot make the baskets identical so return -1
24 if (count % 2) {
25 return -1;
26 }
27
28 // Add the absolute half of the count of each fruit to the disparities array
29 for (let i = Math.abs(count) / 2; i > 0; --i) {
30 disparities.push(fruit);
31 }
32
33 // Update the minimum fruit value
34 minFruit = min(minFruit, fruit);
35 });
36
37 // Sort the disparities array in ascending order
38 disparities.sort((a, b) => a - b);
39
40 const m: number = disparities.length; // Size of the disparities array
41 let totalCost: number = 0; // To store the total cost required to make the baskets identical
42
43 // Loop to calculate the minimum cost required
44 for (let i = 0; i < m / 2; ++i) {
45 // Cost is the minimum between swapping with the cheapest fruit twice or the current disparity
46 totalCost += Math.min(disparities[i], minFruit * 2);
47 }
48
49 // Return the total cost calculated
50 return totalCost;
51}
52
Time and Space Complexity
Time Complexity:
The total time complexity of the given Python code is determined by multiple factors:
-
The loop where we zip
basket1
andbasket2
and updatecnt
: this loop runs for every pair of elements in the two lists, so if each list hasn
elements, this step isO(n)
. -
The
min(cnt)
operation: finding the minimum in the counter object depends on the number of unique elements incnt
, let's denote this number ask
. In the worst case, all elements are unique sok = n
. However, typicallyk
is expected to be much less thann
. This is aO(k)
operation. -
The loop to create
nums
list: the number of iterations is the sum of half the absolute values incnt
(since we're adding abs(v) // 2 elements for each x). In the worst case, this could beO(n)
if every swap generates a new unique number in the counter. -
The
sort()
call onnums
: if we assume thatm
is the number of elements innums
, then sorting would takeO(m log m)
. In the worst case, where we have to addn/2
elements tonums
, this isO(n log n)
. -
The final loop where we sum the minimum of each element and
mi * 2
, runningm/2
times: this isO(m)
which isO(n)
in the worst case.
Adding these together, the time complexity is dominated by the sorting operation, resulting in O(n log n)
.
Space Complexity:
The space complexity is also affected by multiple factors:
-
The Counter
cnt
, which has at mostk
unique elements, wherek
is the number of unique elements. In the worst case,k = n
, so this isO(n)
. -
The
nums
list, which holds up ton/2
elements in the worst case, making itO(n)
. -
The space required for the output of the
min()
operation (a single integer) and the final sum operation, both of which are constant,O(1)
.
Therefore, the combined space complexity of the code is O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
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
LeetCode 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 we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!