Rearranging Fruits
HardPrompt
You have two fruit baskets containing n fruits each. You are given two 0-indexed integer arrays basket1 and basket2 representing the cost of fruit in each basket. You want to make both baskets equal. To do so, you can use the following operation as many times as you want:
- Chose two indices
iandj, and swap theithfruit ofbasket1with thejthfruit ofbasket2. - The cost of the swap is
min(basket1[i],basket2[j]).
Two baskets are considered equal if sorting them according to the fruit cost makes them exactly the same baskets.
Return the minimum cost to make both the baskets equal or -1 if impossible.
Example 1:
Input: basket1 = [4,2,2,2], basket2 = [1,4,1,2]
Output: 1
Explanation: Swap index 1 of basket1 with index 0 of basket2, which has cost 1. Now basket1 = [4,1,2,2] and basket2 = [2,4,1,2]. Rearranging both the arrays makes them equal.Example 2:
Input: basket1 = [2,3,4,1], basket2 = [3,2,5,1]
Output: -1
Explanation: It can be shown that it is impossible to make both the baskets equal.
Constraints:
basket1.length == basket2.length1 <= basket1.length <= 1051 <= basket1[i],basket2[i] <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
This approach models the problem as a classic assignment problem, which can be solved by finding a minimum weight perfect matching in a bipartite graph. First, we determine which fruits are 'misplaced'—that is, which fruits are in excess in one basket and deficient in the other. The set of excess fruits from basket1 forms one partition of our bipartite graph, and the set of excess fruits from basket2 forms the other. The weight of an edge between a fruit c1 from basket1 and a fruit c2 from basket2 is the cost of swapping them. This cost is the minimum of a direct swap, min(c1, c2), and an indirect swap using the cheapest fruit in the system as an intermediary, 2 * min_cost. The problem then reduces to finding a perfect matching between the two sets of fruits that minimizes the total cost (sum of edge weights).
Algorithm
- Count the frequency of each fruit cost in
basket1andbasket2. - Combine the frequencies to get the total count for each fruit cost across both baskets. If any fruit cost has an odd total count, it's impossible to make the baskets equal, so return -1.
- Identify the fruits that need to be moved. Create a list
to_swap_1for fruits in excess inbasket1andto_swap_2for fruits in excess inbasket2. Let the number of fruits to be swapped bek. - Find the minimum fruit cost
min_costpresent in either basket. - Construct a
k x kcost matrixC, whereC[i][j]is the cost of swapping thei-th fruit fromto_swap_1with thej-th fruit fromto_swap_2. The cost ismin(min(to_swap_1[i], to_swap_2[j]), 2 * min_cost). - Use an algorithm for the assignment problem, like the Hungarian algorithm, to find the minimum weight perfect matching in the bipartite graph represented by the cost matrix.
- The result of the assignment problem is the minimum total cost.
Walkthrough
The core idea is to formally define the swapping problem in terms of graph theory. We need to perform a series of swaps to balance the baskets. Each swap involves one fruit that basket1 needs to get rid of and one fruit that basket2 needs to get rid of. This is a one-to-one assignment.
We can represent the fruits to be moved from basket1 as one set of vertices, and those from basket2 as another. A complete bipartite graph is formed where edges represent potential swaps. The weight of each edge is the cost of that specific swap. The goal is to select a set of k edges (a perfect matching) such that every vertex is touched exactly once and the sum of the weights of these edges is minimized. This is a standard problem in computer science known as the assignment problem or min-weight bipartite matching, often solved with the Hungarian algorithm or min-cost max-flow algorithms. While correct, this method is not practical for the given constraints due to its high computational complexity.
// This is a conceptual illustration. A full implementation of the Hungarian// algorithm is complex and not provided here due to its inefficiency for this problem.import java.util.*; class Solution { public long minCost(int[] basket1, int[] basket2) { // Step 1 & 2: Count frequencies and check possibility Map<Integer, Integer> counts = new HashMap<>(); for (int fruit : basket1) counts.put(fruit, counts.getOrDefault(fruit, 0) + 1); for (int fruit : basket2) counts.put(fruit, counts.getOrDefault(fruit, 0) + 1); for (int count : counts.values()) { if (count % 2 != 0) { return -1; } } // Step 3: Identify fruits to swap Map<Integer, Integer> counts1 = new HashMap<>(); for (int fruit : basket1) counts1.put(fruit, counts1.getOrDefault(fruit, 0) + 1); List<Integer> to_swap_1 = new ArrayList<>(); List<Integer> to_swap_2 = new ArrayList<>(); for (Map.Entry<Integer, Integer> entry : counts.entrySet()) { int fruit = entry.getKey(); int totalCount = entry.getValue(); int count1 = counts1.getOrDefault(fruit, 0); int diff = count1 - totalCount / 2; if (diff > 0) { for (int i = 0; i < diff; i++) to_swap_1.add(fruit); } else if (diff < 0) { for (int i = 0; i < -diff; i++) to_swap_2.add(fruit); } } // If no swaps needed, cost is 0 if (to_swap_1.isEmpty()) { return 0; } // Step 4, 5, 6: This is where the heavyweight algorithm would be used. // The following is a placeholder for a call to a hypothetical Hungarian algorithm solver. // long totalCost = solveAssignmentProblem(to_swap_1, to_swap_2); // return totalCost; // Due to the high complexity, this approach is not feasible and a greedy approach is preferred. return -1; // Placeholder for non-implementation } // A hypothetical solver for the assignment problem. // private long solveAssignmentProblem(List<Integer> list1, List<Integer> list2) { // // ... implementation of Hungarian Algorithm O(k^3) ... // }}Complexity
Time
O(k^3) using a standard implementation of the Hungarian algorithm, where k is the number of swaps. Since k can be up to N/2, the complexity is O(N^3) in the worst case.
Space
O(k^2), where k is the number of items to swap. In the worst case, k can be O(N), leading to O(N^2) space for the cost matrix.
Trade-offs
Pros
Provides a correct and general framework for solving assignment-type problems.
Guaranteed to find the optimal solution.
Cons
The time complexity of O(N^3) is too high for the given constraints (N up to 10^5), and will result in a 'Time Limit Exceeded' error.
The space complexity of O(N^2) can also be prohibitive for large N.
It's an overly complicated solution for a problem that has a much simpler and more efficient greedy solution.
A more efficient method is a greedy approach. The core insight is that to minimize the total swap cost, we should be strategic about which fruits we pair up for swapping. The problem of minimizing the sum of min(c1, c2) over all pairs is solved by pairing the smallest-cost fruits from one swap list with the largest-cost fruits from the other. This is a classic result related to the rearrangement inequality.
This approach first identifies the multisets of fruits that need to be moved from basket1 to basket2 and vice-versa. Then, it sorts one list of costs ascendingly and the other descendingly. By pairing the elements at the same index from these two sorted lists, we ensure an optimal pairing for direct swaps. For each such pair, we also consider a second option: instead of a direct swap, we can use the globally cheapest fruit as an intermediary, which costs 2 * min_cost. We take the cheaper of these two options for each pair of fruits that need to be swapped.
Algorithm
- Use two HashMaps,
counts1andcounts2, to store the frequency of each fruit cost inbasket1andbasket2respectively. Also, find the global minimum fruit cost,min_cost. - Create a combined frequency map for all fruits. Iterate through this map to check if all fruit types have an even total count. If any count is odd, return -1.
- Create two lists,
to_swap_1andto_swap_2, to hold the costs of fruits that need to be moved out ofbasket1andbasket2respectively. - Iterate through the combined frequency map. For each fruit
c, calculate how manyc's are in excess in one basket. Addcto the appropriate swap list that many times. For example, ifbasket1hasdexcess fruits of costc, addctoto_swap_1dtimes. - Sort
to_swap_1in ascending order andto_swap_2in descending order. This is the crucial greedy step. - Initialize
total_cost = 0. - Iterate from
i = 0tok-1(wherekis the number of swaps). In each iteration, we pair thei-th element ofto_swap_1with thei-th element ofto_swap_2. - The cost for swapping this pair
(c1, c2)ismin(min(c1, c2), 2 * min_cost). The2 * min_costterm represents the alternative of using the cheapest fruit as an intermediary for two separate swaps. - Add this cost to
total_cost. - Return
total_cost.
Walkthrough
First, we must check if it's possible to make the baskets equal. This is done by counting the total frequency of each fruit cost. If any fruit cost appears an odd number of times in total, we can't distribute it evenly, so we return -1. Otherwise, a solution exists.
We then determine the 'swap lists': to_swap_1 contains costs of fruits that basket1 has in excess, and to_swap_2 for basket2. To minimize the sum of swap costs, we apply a greedy strategy. Let's say we need to swap k fruits. We sort to_swap_1 ascendingly and to_swap_2 descendingly. Then we iterate through them, pairing to_swap_1[i] with to_swap_2[i]. The cost for this pair is min(to_swap_1[i], to_swap_2[i]). However, we can always use the globally cheapest fruit (min_cost) as an intermediary. Swapping a fruit c with min_cost costs min_cost. To exchange a fruit from basket1 and another from basket2, we can do two such swaps via min_cost, for a total cost of 2 * min_cost. So, for each pair, we take min(direct_swap_cost, 2 * min_cost). Summing these up gives the total minimum cost.
import java.util.*; class Solution { public long minCost(int[] basket1, int[] basket2) { Map<Integer, Integer> allCounts = new HashMap<>(); int minCost = Integer.MAX_VALUE; for (int fruit : basket1) { allCounts.put(fruit, allCounts.getOrDefault(fruit, 0) + 1); minCost = Math.min(minCost, fruit); } for (int fruit : basket2) { allCounts.put(fruit, allCounts.getOrDefault(fruit, 0) + 1); minCost = Math.min(minCost, fruit); } for (int count : allCounts.values()) { if (count % 2 != 0) { return -1; } } Map<Integer, Integer> counts1 = new HashMap<>(); for (int fruit : basket1) { counts1.put(fruit, counts1.getOrDefault(fruit, 0) + 1); } List<Integer> toSwap1 = new ArrayList<>(); List<Integer> toSwap2 = new ArrayList<>(); for (Map.Entry<Integer, Integer> entry : allCounts.entrySet()) { int fruit = entry.getKey(); int totalCount = entry.getValue(); int count1 = counts1.getOrDefault(fruit, 0); int diff = count1 - totalCount / 2; if (diff > 0) { for (int i = 0; i < diff; i++) { toSwap1.add(fruit); } } else if (diff < 0) { for (int i = 0; i < -diff; i++) { toSwap2.add(fruit); } } } Collections.sort(toSwap1); Collections.sort(toSwap2, Collections.reverseOrder()); long cost = 0; for (int i = 0; i < toSwap1.size(); i++) { int c1 = toSwap1.get(i); int c2 = toSwap2.get(i); cost += Math.min(Math.min(c1, c2), 2 * minCost); } return cost; }}Complexity
Time
O(N log N), dominated by the sorting of the swap lists. Populating frequency maps and iterating through them takes O(N) time. The size of the swap lists can be at most N, so sorting takes O(N log N).
Space
O(N) in the worst case. The frequency maps can store up to O(N) unique elements, and the swap lists can contain up to O(N) elements in total.
Trade-offs
Pros
Highly efficient with O(N log N) time complexity, which passes the given constraints.
The greedy strategy is proven to be optimal for this problem structure.
Implementation is relatively straightforward using standard library data structures and sorting.
Cons
The logic involving the
2 * min_costcan be subtle to reason about.Requires sorting, which adds an O(N log N) factor to the time complexity.
Solutions
Solution
class Solution {public long minCost(int[] basket1, int[] basket2) { int n = basket1.length; Map<Integer, Integer> cnt = new HashMap<>(); for (int i = 0; i < n; ++i) { cnt.merge(basket1[i], 1, Integer : : sum); cnt.merge(basket2[i], -1, Integer : : sum); } int mi = 1 << 30; List<Integer> nums = new ArrayList<>(); for (var e : cnt.entrySet()) { int x = e.getKey(), v = e.getValue(); if (v % 2 != 0) { return -1; } for (int i = Math.abs(v) / 2; i > 0; --i) { nums.add(x); } mi = Math.min(mi, x); } Collections.sort(nums); int m = nums.size(); long ans = 0; for (int i = 0; i < m / 2; ++i) { ans += Math.min(nums.get(i), mi * 2); } return ans; }}class Solution {public: long long minCost(vector<int> &basket1, vector<int> &basket2) { int n = basket1.size(); unordered_map<int, int> cnt; for (int i = 0; i < n; ++i) { cnt[basket1[i]]++; cnt[basket2[i]]--; } int mi = 1 << 30; vector<int> nums; for (auto &[x, v] : cnt) { if (v % 2) { return -1; } for (int i = abs(v) / 2; i; --i) { nums.push_back(x); } mi = min(mi, x); } sort(nums.begin(), nums.end()); int m = nums.size(); long long ans = 0; for (int i = 0; i < m / 2; ++i) { ans += min(nums[i], mi * 2); } return ans; }};class Solution: def minCost(self, basket1: List[int], basket2: List[int]) -> int: cnt = Counter() for a, b in zip(basket1, basket2): cnt[a] += 1 cnt[b] -= 1 mi = min(cnt) nums = [] for x, v in cnt . items(): if v % 2: return - 1 nums . extend([x] * (abs(v) // 2)) nums . sort() m = len(nums) // 2 return sum(min(x, mi * 2) for x in nums[: m])Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.