Skip to content

Commit 4f56c50

Browse files
Add solution for LeetCode #2857: Rearranging Fruits
1 parent e5dd08e commit 4f56c50

File tree

3 files changed

+326
-0
lines changed

3 files changed

+326
-0
lines changed
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Rearranging Fruits - Problem #2857
2+
3+
## Problem Statement
4+
You have two fruit baskets and `n` fruits. You are given two 0-indexed integer arrays `basket1` and `basket2` representing the cost of fruit in basket1 and basket2 respectively. You want to make both baskets equal. To do so, you can use the following operation as many times as you want:
5+
6+
- Chose two indices `i` and `j`, and swap the `i`th fruit of basket1 with the `j`th fruit of basket2. The cost of this operation is `min(basket1[i], basket2[j])`.
7+
8+
Return the minimum cost to make both baskets equal. If it is impossible to make both baskets equal, return `-1`.
9+
10+
## Examples
11+
```
12+
Input: basket1 = [4,2,2,2], basket2 = [1,4,1,2]
13+
Output: 1
14+
Explanation: Swap index 1 of basket1 with index 0 of basket2, which costs 1. Now both baskets have equal sum.
15+
16+
Input: basket1 = [2,3,4,1], basket2 = [3,2,5,1]
17+
Output: -1
18+
Explanation: It can be shown that it is impossible to make both baskets equal.
19+
```
20+
21+
## Approach
22+
**Key Insight**: To make both baskets equal, the total sum of both baskets must be equal. If they're not equal, it's impossible. If they are equal, we need to find the minimum cost to rearrange fruits.
23+
24+
**Algorithm**:
25+
1. Check if the sum of both baskets is equal. If not, return -1.
26+
2. Count the frequency of each fruit in both baskets.
27+
3. For each fruit type, if the total count is odd, return -1 (impossible to split equally).
28+
4. Calculate the excess/deficit for each fruit type.
29+
5. Sort the excess and deficit arrays.
30+
6. Use two pointers to match the smallest excess with the smallest deficit, minimizing the cost.
31+
32+
**Why this works**:
33+
- We need to balance the fruits between baskets
34+
- The minimum cost is achieved by matching the smallest excess with the smallest deficit
35+
- Each swap operation costs the minimum of the two fruits being swapped
36+
37+
## Complexity Analysis
38+
- **Time Complexity**: O(n log n) - Due to sorting the excess and deficit arrays
39+
- **Space Complexity**: O(n) - To store frequency counts and excess/deficit arrays
40+
41+
## Key Insights
42+
- Both baskets must have equal total sum
43+
- Each fruit type must have even total count
44+
- Optimal matching is greedy: smallest excess with smallest deficit
45+
- The cost of a swap is the minimum of the two fruits being swapped
46+
47+
## Alternative Approaches
48+
1. **Brute Force**: Try all possible combinations - O(n!) time, impractical
49+
2. **Dynamic Programming**: Can be used but overkill for this problem
50+
3. **Graph-based**: Model as a matching problem, but greedy approach is simpler
51+
52+
## Solutions in Different Languages
53+
54+
### Java
55+
```java
56+
// See solution.java
57+
import java.util.*;
58+
59+
class Solution {
60+
public long minCost(int[] basket1, int[] basket2) {
61+
// Check if sums are equal
62+
long sum1 = 0, sum2 = 0;
63+
for (int fruit : basket1) sum1 += fruit;
64+
for (int fruit : basket2) sum2 += fruit;
65+
66+
if (sum1 != sum2) return -1;
67+
68+
// Count frequencies
69+
Map<Integer, Integer> freq1 = new HashMap<>();
70+
Map<Integer, Integer> freq2 = new HashMap<>();
71+
72+
for (int fruit : basket1) freq1.put(fruit, freq1.getOrDefault(fruit, 0) + 1);
73+
for (int fruit : basket2) freq2.put(fruit, freq2.getOrDefault(fruit, 0) + 1);
74+
75+
// Check if each fruit type has even total count
76+
Set<Integer> allFruits = new HashSet<>();
77+
allFruits.addAll(freq1.keySet());
78+
allFruits.addAll(freq2.keySet());
79+
80+
for (int fruit : allFruits) {
81+
int total = freq1.getOrDefault(fruit, 0) + freq2.getOrDefault(fruit, 0);
82+
if (total % 2 != 0) return -1;
83+
}
84+
85+
// Calculate excess/deficit
86+
List<Integer> excess = new ArrayList<>();
87+
List<Integer> deficit = new ArrayList<>();
88+
89+
for (int fruit : allFruits) {
90+
int count1 = freq1.getOrDefault(fruit, 0);
91+
int count2 = freq2.getOrDefault(fruit, 0);
92+
int target = (count1 + count2) / 2;
93+
94+
if (count1 > target) {
95+
for (int i = 0; i < count1 - target; i++) {
96+
excess.add(fruit);
97+
}
98+
} else if (count2 > target) {
99+
for (int i = 0; i < count2 - target; i++) {
100+
deficit.add(fruit);
101+
}
102+
}
103+
}
104+
105+
// Sort for optimal matching
106+
Collections.sort(excess);
107+
Collections.sort(deficit);
108+
109+
long cost = 0;
110+
for (int i = 0; i < excess.size(); i++) {
111+
cost += Math.min(excess.get(i), deficit.get(i));
112+
}
113+
114+
return cost;
115+
}
116+
}
117+
```
118+
119+
### JavaScript
120+
```javascript
121+
// See solution.js
122+
/**
123+
* @param {number[]} basket1
124+
* @param {number[]} basket2
125+
* @return {number}
126+
*/
127+
var minCost = function(basket1, basket2) {
128+
// Check if sums are equal
129+
const sum1 = basket1.reduce((sum, fruit) => sum + fruit, 0);
130+
const sum2 = basket2.reduce((sum, fruit) => sum + fruit, 0);
131+
132+
if (sum1 !== sum2) return -1;
133+
134+
// Count frequencies
135+
const freq1 = new Map();
136+
const freq2 = new Map();
137+
138+
for (const fruit of basket1) {
139+
freq1.set(fruit, (freq1.get(fruit) || 0) + 1);
140+
}
141+
for (const fruit of basket2) {
142+
freq2.set(fruit, (freq2.get(fruit) || 0) + 1);
143+
}
144+
145+
// Check if each fruit type has even total count
146+
const allFruits = new Set([...freq1.keys(), ...freq2.keys()]);
147+
148+
for (const fruit of allFruits) {
149+
const total = (freq1.get(fruit) || 0) + (freq2.get(fruit) || 0);
150+
if (total % 2 !== 0) return -1;
151+
}
152+
153+
// Calculate excess/deficit
154+
const excess = [];
155+
const deficit = [];
156+
157+
for (const fruit of allFruits) {
158+
const count1 = freq1.get(fruit) || 0;
159+
const count2 = freq2.get(fruit) || 0;
160+
const target = (count1 + count2) / 2;
161+
162+
if (count1 > target) {
163+
for (let i = 0; i < count1 - target; i++) {
164+
excess.push(fruit);
165+
}
166+
} else if (count2 > target) {
167+
for (let i = 0; i < count2 - target; i++) {
168+
deficit.push(fruit);
169+
}
170+
}
171+
}
172+
173+
// Sort for optimal matching
174+
excess.sort((a, b) => a - b);
175+
deficit.sort((a, b) => a - b);
176+
177+
let cost = 0;
178+
for (let i = 0; i < excess.length; i++) {
179+
cost += Math.min(excess[i], deficit[i]);
180+
}
181+
182+
return cost;
183+
};
184+
```
185+
186+
## Test Cases
187+
```
188+
Test Case 1: basket1 = [4,2,2,2], basket2 = [1,4,1,2] → 1
189+
Test Case 2: basket1 = [2,3,4,1], basket2 = [3,2,5,1] → -1
190+
Test Case 3: basket1 = [1,1,1,1], basket2 = [1,1,1,1] → 0
191+
Test Case 4: basket1 = [1,2,3], basket2 = [1,2,3] → 0
192+
```
193+
194+
## Edge Cases
195+
- Baskets already equal (cost = 0)
196+
- Impossible to make equal (return -1)
197+
- Single fruit in each basket
198+
- All fruits are the same
199+
- Large numbers (use long in Java)
200+
201+
## Related Problems
202+
- Two Sum
203+
- Minimum Swaps to Make Sequences Equal
204+
- Partition Equal Subset Sum
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import java.util.*;
2+
3+
class Solution {
4+
public long minCost(int[] basket1, int[] basket2) {
5+
// Check if sums are equal
6+
long sum1 = 0, sum2 = 0;
7+
for (int fruit : basket1) sum1 += fruit;
8+
for (int fruit : basket2) sum2 += fruit;
9+
10+
if (sum1 != sum2) return -1;
11+
12+
// Count frequencies
13+
Map<Integer, Integer> freq1 = new HashMap<>();
14+
Map<Integer, Integer> freq2 = new HashMap<>();
15+
16+
for (int fruit : basket1) freq1.put(fruit, freq1.getOrDefault(fruit, 0) + 1);
17+
for (int fruit : basket2) freq2.put(fruit, freq2.getOrDefault(fruit, 0) + 1);
18+
19+
// Check if each fruit type has even total count
20+
Set<Integer> allFruits = new HashSet<>();
21+
allFruits.addAll(freq1.keySet());
22+
allFruits.addAll(freq2.keySet());
23+
24+
for (int fruit : allFruits) {
25+
int total = freq1.getOrDefault(fruit, 0) + freq2.getOrDefault(fruit, 0);
26+
if (total % 2 != 0) return -1;
27+
}
28+
29+
// Calculate excess/deficit
30+
List<Integer> excess = new ArrayList<>();
31+
List<Integer> deficit = new ArrayList<>();
32+
33+
for (int fruit : allFruits) {
34+
int count1 = freq1.getOrDefault(fruit, 0);
35+
int count2 = freq2.getOrDefault(fruit, 0);
36+
int target = (count1 + count2) / 2;
37+
38+
if (count1 > target) {
39+
for (int i = 0; i < count1 - target; i++) {
40+
excess.add(fruit);
41+
}
42+
} else if (count2 > target) {
43+
for (int i = 0; i < count2 - target; i++) {
44+
deficit.add(fruit);
45+
}
46+
}
47+
}
48+
49+
// Sort for optimal matching
50+
Collections.sort(excess);
51+
Collections.sort(deficit);
52+
53+
long cost = 0;
54+
for (int i = 0; i < excess.size(); i++) {
55+
cost += Math.min(excess.get(i), deficit.get(i));
56+
}
57+
58+
return cost;
59+
}
60+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @param {number[]} basket1
3+
* @param {number[]} basket2
4+
* @return {number}
5+
*/
6+
var minCost = function(basket1, basket2) {
7+
// Check if sums are equal
8+
const sum1 = basket1.reduce((sum, fruit) => sum + fruit, 0);
9+
const sum2 = basket2.reduce((sum, fruit) => sum + fruit, 0);
10+
11+
if (sum1 !== sum2) return -1;
12+
13+
// Count frequencies
14+
const freq1 = new Map();
15+
const freq2 = new Map();
16+
17+
for (const fruit of basket1) {
18+
freq1.set(fruit, (freq1.get(fruit) || 0) + 1);
19+
}
20+
for (const fruit of basket2) {
21+
freq2.set(fruit, (freq2.get(fruit) || 0) + 1);
22+
}
23+
24+
// Check if each fruit type has even total count
25+
const allFruits = new Set([...freq1.keys(), ...freq2.keys()]);
26+
27+
for (const fruit of allFruits) {
28+
const total = (freq1.get(fruit) || 0) + (freq2.get(fruit) || 0);
29+
if (total % 2 !== 0) return -1;
30+
}
31+
32+
// Calculate excess/deficit
33+
const excess = [];
34+
const deficit = [];
35+
36+
for (const fruit of allFruits) {
37+
const count1 = freq1.get(fruit) || 0;
38+
const count2 = freq2.get(fruit) || 0;
39+
const target = (count1 + count2) / 2;
40+
41+
if (count1 > target) {
42+
for (let i = 0; i < count1 - target; i++) {
43+
excess.push(fruit);
44+
}
45+
} else if (count2 > target) {
46+
for (let i = 0; i < count2 - target; i++) {
47+
deficit.push(fruit);
48+
}
49+
}
50+
}
51+
52+
// Sort for optimal matching
53+
excess.sort((a, b) => a - b);
54+
deficit.sort((a, b) => a - b);
55+
56+
let cost = 0;
57+
for (let i = 0; i < excess.length; i++) {
58+
cost += Math.min(excess[i], deficit[i]);
59+
}
60+
61+
return cost;
62+
};

0 commit comments

Comments
 (0)