2517. Maximum Tastiness of Candy Basket
Problem Description
You have an array price containing positive integers, where price[i] represents the price of the i-th candy. You also have a positive integer k.
The store sells baskets containing exactly k distinct candies. The tastiness of a candy basket is defined as the smallest absolute difference between the prices of any two candies in that basket.
Your task is to find the maximum possible tastiness value when selecting k distinct candies from the available candies.
Example to understand tastiness:
- If you have a basket with candies priced at [1, 5, 9], the absolute differences between pairs are:
- |5 - 1| = 4
- |9 - 5| = 4
- |9 - 1| = 8
- The tastiness would be min(4, 4, 8) = 4
The goal is to select k candies such that the minimum pairwise price difference is as large as possible.
Key Points:
- You must select exactly
kdistinct candies - The tastiness is the minimum difference between any two selected candies
- You want to maximize this tastiness value
- The candies are distinct (you can't pick the same candy twice)
Intuition
When we need to maximize the minimum difference between selected candies, we want to spread out our selections as much as possible. Think of it like placing k points on a number line - to maximize the minimum distance between any two points, we'd want to space them out evenly.
The key insight is that if we sort the prices first, we can transform this into a different problem: instead of finding which candies to select, we can ask "given a minimum difference x, can we select k candies such that every pair has at least x difference?"
This reframing leads us to binary search on the answer. Why? Because:
- If we can achieve a tastiness of
x, we can also achieve any tastiness less thanx(by selecting the same candies) - If we cannot achieve a tastiness of
x, we cannot achieve any tastiness greater thanx
This monotonic property makes binary search perfect for this problem.
The search space for our answer is between:
- Minimum:
0(worst case, though we could have identical prices) - Maximum:
price[-1] - price[0](the difference between the most and least expensive candy after sorting)
For each candidate tastiness value mid, we greedily check if we can select k candies with minimum difference of at least mid. The greedy strategy works because once we sort the prices, we can always start with the first candy and keep selecting the next candy that is at least mid distance away. If we can find k such candies, then mid is achievable; otherwise, we need to try a smaller value.
The greedy selection is optimal because selecting candies that are closer together than necessary doesn't help us - we want to use our "budget" of candies efficiently by spreading them out as much as the current mid value requires.
Learn more about Greedy, Binary Search and Sorting patterns.
Solution Implementation
1class Solution:
2 def maximumTastiness(self, price: List[int], k: int) -> int:
3 """
4 Find the maximum minimum difference between any two chosen prices.
5 Uses binary search on the answer with a greedy validation function.
6
7 Args:
8 price: List of candy prices
9 k: Number of candies to select
10
11 Returns:
12 Maximum possible minimum difference between selected candies
13 """
14
15 def feasible(min_diff: int) -> bool:
16 """
17 Check if we CANNOT select k candies with minimum difference >= min_diff.
18 This is the inverted condition for the first_true_index template.
19
20 Args:
21 min_diff: The minimum difference to maintain between selected candies
22
23 Returns:
24 True if k candies CANNOT be selected (infeasible)
25 """
26 count = 0
27 last_selected = -min_diff # Initialize to ensure first candy can always be selected
28
29 for current_price in price:
30 # Select current candy if it's far enough from the last selected one
31 if current_price - last_selected >= min_diff:
32 last_selected = current_price
33 count += 1
34
35 # Return True when infeasible (cannot select k candies)
36 return count < k
37
38 # Sort prices to enable greedy selection
39 price.sort()
40
41 # Binary search using first_true_index template
42 # We search for the first value where feasible returns True (infeasible)
43 # The answer is first_true_index - 1
44 left = 0
45 right = price[-1] - price[0] + 1 # +1 to include the boundary
46 first_true_index = -1
47
48 while left <= right:
49 mid = (left + right) // 2
50 if feasible(mid):
51 # Found an infeasible point, record it and search left
52 first_true_index = mid
53 right = mid - 1
54 else:
55 # Still feasible, search right for first infeasible
56 left = mid + 1
57
58 # The maximum feasible value is first_true_index - 1
59 return first_true_index - 1 if first_true_index > 0 else 0
601class Solution {
2 /**
3 * Finds the maximum minimum difference (tastiness) when selecting k items from the price array.
4 * Uses binary search with the first_true_index template pattern.
5 *
6 * @param price Array of item prices
7 * @param k Number of items to select
8 * @return Maximum possible minimum difference between any two selected items
9 */
10 public int maximumTastiness(int[] price, int k) {
11 // Sort the price array to enable binary search on differences
12 Arrays.sort(price);
13
14 // Binary search using first_true_index template
15 // We search for the first infeasible value, then subtract 1
16 int left = 0;
17 int right = price[price.length - 1] - price[0] + 1;
18 int firstTrueIndex = -1;
19
20 while (left <= right) {
21 int mid = left + (right - left) / 2;
22
23 // Check if we CANNOT select k items with minimum difference of at least mid
24 if (isInfeasible(price, k, mid)) {
25 // Found an infeasible point, record it and search left
26 firstTrueIndex = mid;
27 right = mid - 1;
28 } else {
29 // Still feasible, search right for first infeasible
30 left = mid + 1;
31 }
32 }
33
34 // The maximum feasible value is firstTrueIndex - 1
35 return firstTrueIndex > 0 ? firstTrueIndex - 1 : 0;
36 }
37
38 /**
39 * Checks if it's IMPOSSIBLE to select k items with minimum difference of at least minDifference.
40 * This is the inverted condition for the first_true_index template.
41 *
42 * @param price Sorted array of item prices
43 * @param k Number of items to select
44 * @param minDifference Minimum required difference between consecutive selected items
45 * @return true if k items CANNOT be selected (infeasible), false otherwise
46 */
47 private boolean isInfeasible(int[] price, int k, int minDifference) {
48 int selectedCount = 0;
49 int previousPrice = -minDifference; // Initialize to ensure first item can be selected
50
51 // Greedily select items that maintain the minimum difference
52 for (int currentPrice : price) {
53 if (currentPrice - previousPrice >= minDifference) {
54 // Select this item as it satisfies the minimum difference constraint
55 previousPrice = currentPrice;
56 selectedCount++;
57 }
58 }
59
60 // Return true when infeasible (cannot select k items)
61 return selectedCount < k;
62 }
63}
641class Solution {
2public:
3 int maximumTastiness(vector<int>& price, int k) {
4 // Sort prices in ascending order for binary search approach
5 sort(price.begin(), price.end());
6
7 // Binary search using first_true_index template
8 // We search for the first infeasible value, then subtract 1
9 int left = 0;
10 int right = price.back() - price[0] + 1;
11 int firstTrueIndex = -1;
12
13 // Helper function to check if we CANNOT select k items with minimum difference of minDiff
14 auto isInfeasible = [&](int minDiff) -> bool {
15 int selectedCount = 0;
16 int previousSelected = -minDiff; // Initialize to ensure first item can be selected
17
18 // Greedily select items that maintain at least minDiff distance
19 for (int& currentPrice : price) {
20 if (currentPrice - previousSelected >= minDiff) {
21 previousSelected = currentPrice;
22 ++selectedCount;
23 }
24 }
25
26 // Return true when infeasible (cannot select k items)
27 return selectedCount < k;
28 };
29
30 // Binary search for the first infeasible value
31 while (left <= right) {
32 int mid = left + (right - left) / 2;
33
34 if (isInfeasible(mid)) {
35 // Found an infeasible point, record it and search left
36 firstTrueIndex = mid;
37 right = mid - 1;
38 } else {
39 // Still feasible, search right for first infeasible
40 left = mid + 1;
41 }
42 }
43
44 // The maximum feasible value is firstTrueIndex - 1
45 return firstTrueIndex > 0 ? firstTrueIndex - 1 : 0;
46 }
47};
481/**
2 * Finds the maximum minimum difference between k selected prices
3 * Uses the first_true_index template pattern with inverted condition.
4 * @param price - Array of prices to choose from
5 * @param k - Number of prices to select
6 * @returns Maximum possible minimum difference between selected prices
7 */
8function maximumTastiness(price: number[], k: number): number {
9 // Sort prices in ascending order for binary search approach
10 price.sort((a, b) => a - b);
11
12 // Binary search using first_true_index template
13 // We search for the first infeasible value, then subtract 1
14 let left = 0;
15 let right = price[price.length - 1] - price[0] + 1;
16 let firstTrueIndex = -1;
17
18 /**
19 * Checks if it's IMPOSSIBLE to select k prices with minimum difference of at least minDifference
20 * This is the inverted condition for the first_true_index template.
21 * @param minDifference - The minimum difference to maintain between selected prices
22 * @returns True if k prices CANNOT be selected (infeasible)
23 */
24 const isInfeasible = (minDifference: number): boolean => {
25 // Initialize count and previous selected price
26 let selectedCount = 0;
27 let previousPrice = -minDifference; // Ensures first price can always be selected
28
29 // Greedily select prices that maintain minimum difference
30 for (const currentPrice of price) {
31 if (currentPrice - previousPrice >= minDifference) {
32 previousPrice = currentPrice;
33 selectedCount++;
34 }
35 }
36
37 // Return true when infeasible (cannot select k prices)
38 return selectedCount < k;
39 };
40
41 // Binary search for the first infeasible value
42 while (left <= right) {
43 const mid = Math.floor((left + right) / 2);
44
45 if (isInfeasible(mid)) {
46 // Found an infeasible point, record it and search left
47 firstTrueIndex = mid;
48 right = mid - 1;
49 } else {
50 // Still feasible, search right for first infeasible
51 left = mid + 1;
52 }
53 }
54
55 // The maximum feasible value is firstTrueIndex - 1
56 return firstTrueIndex > 0 ? firstTrueIndex - 1 : 0;
57}
58Solution Approach
The solution uses binary search with the first_true_index template pattern combined with a greedy validation function.
Key Insight for Template Adaptation: This is a maximization problem where we want the largest feasible value. To use the first_true_index template, we invert the condition: instead of "can we achieve tastiness x?", we ask "is x infeasible?". Then we find the first infeasible value and subtract 1.
Step 1: Sort the prices
price.sort()
Sorting allows us to work with candies in order of increasing price.
Step 2: Set up binary search with the template
left = 0 right = price[-1] - price[0] + 1 # +1 to include the boundary first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid): # Returns True when infeasible first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1
Step 3: The inverted feasible function
def feasible(min_diff: int) -> bool:
count = 0
last_selected = -min_diff
for current_price in price:
if current_price - last_selected >= min_diff:
last_selected = current_price
count += 1
# Return True when infeasible (cannot select k candies)
return count < k
This greedy function returns True when we CANNOT select k candies with minimum difference of at least x. The pattern of values is: false, false, ..., true, true, ... where the first true marks the first infeasible value.
The algorithm works because:
- We find
first_true_index: the smallest value where we cannot achieve the tastiness - The maximum feasible tastiness is
first_true_index - 1 - This maintains the canonical template structure while solving a maximization problem
Time Complexity: O(n log(max_price - min_price)) where n is the length of the price array
Space Complexity: O(1) excluding the sorting 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 an example with price = [13, 5, 1, 8, 21, 2] and k = 3.
Step 1: Sort the prices
After sorting: price = [1, 2, 5, 8, 13, 21]
Step 2: Initialize binary search boundaries
l = 0(minimum possible tastiness)r = 21 - 1 = 20(maximum possible tastiness)
Step 3: Binary search iterations
Iteration 1:
mid = (0 + 20 + 1) >> 1 = 10- Check if we can achieve tastiness of 10:
- Start with
pre = -10,cnt = 0 - Candy at price 1:
1 - (-10) = 11 >= 10✓ Select it.pre = 1,cnt = 1 - Candy at price 2:
2 - 1 = 1 < 10✗ Skip - Candy at price 5:
5 - 1 = 4 < 10✗ Skip - Candy at price 8:
8 - 1 = 7 < 10✗ Skip - Candy at price 13:
13 - 1 = 12 >= 10✓ Select it.pre = 13,cnt = 2 - Candy at price 21:
21 - 13 = 8 < 10✗ Skip - Result:
cnt = 2 < 3→ Cannot achieve tastiness of 10
- Start with
- Update:
r = 10 - 1 = 9
Iteration 2:
mid = (0 + 9 + 1) >> 1 = 5- Check if we can achieve tastiness of 5:
- Start with
pre = -5,cnt = 0 - Candy at price 1:
1 - (-5) = 6 >= 5✓ Select it.pre = 1,cnt = 1 - Candy at price 2:
2 - 1 = 1 < 5✗ Skip - Candy at price 5:
5 - 1 = 4 < 5✗ Skip - Candy at price 8:
8 - 1 = 7 >= 5✓ Select it.pre = 8,cnt = 2 - Candy at price 13:
13 - 8 = 5 >= 5✓ Select it.pre = 13,cnt = 3 - Candy at price 21: We already have 3 candies
- Result:
cnt = 3 >= 3→ Can achieve tastiness of 5
- Start with
- Update:
l = 5
Iteration 3:
mid = (5 + 9 + 1) >> 1 = 7- Check if we can achieve tastiness of 7:
- Start with
pre = -7,cnt = 0 - Candy at price 1:
1 - (-7) = 8 >= 7✓ Select it.pre = 1,cnt = 1 - Candy at price 2:
2 - 1 = 1 < 7✗ Skip - Candy at price 5:
5 - 1 = 4 < 7✗ Skip - Candy at price 8:
8 - 1 = 7 >= 7✓ Select it.pre = 8,cnt = 2 - Candy at price 13:
13 - 8 = 5 < 7✗ Skip - Candy at price 21:
21 - 8 = 13 >= 7✓ Select it.pre = 21,cnt = 3 - Result:
cnt = 3 >= 3→ Can achieve tastiness of 7
- Start with
- Update:
l = 7
Iteration 4:
mid = (7 + 9 + 1) >> 1 = 8- Check if we can achieve tastiness of 8:
- Start with
pre = -8,cnt = 0 - Candy at price 1:
1 - (-8) = 9 >= 8✓ Select it.pre = 1,cnt = 1 - Candy at price 2:
2 - 1 = 1 < 8✗ Skip - Candy at price 5:
5 - 1 = 4 < 8✗ Skip - Candy at price 8:
8 - 1 = 7 < 8✗ Skip - Candy at price 13:
13 - 1 = 12 >= 8✓ Select it.pre = 13,cnt = 2 - Candy at price 21:
21 - 13 = 8 >= 8✓ Select it.pre = 21,cnt = 3 - Result:
cnt = 3 >= 3→ Can achieve tastiness of 8
- Start with
- Update:
l = 8
Iteration 5:
mid = (8 + 9 + 1) >> 1 = 9- Check if we can achieve tastiness of 9:
- Start with
pre = -9,cnt = 0 - Candy at price 1:
1 - (-9) = 10 >= 9✓ Select it.pre = 1,cnt = 1 - Candy at price 2:
2 - 1 = 1 < 9✗ Skip - Candy at price 5:
5 - 1 = 4 < 9✗ Skip - Candy at price 8:
8 - 1 = 7 < 9✗ Skip - Candy at price 13:
13 - 1 = 12 >= 9✓ Select it.pre = 13,cnt = 2 - Candy at price 21:
21 - 13 = 8 < 9✗ Skip - Result:
cnt = 2 < 3→ Cannot achieve tastiness of 9
- Start with
- Update:
r = 9 - 1 = 8
Final result: l = r = 8
The maximum tastiness is 8, achieved by selecting candies with prices [1, 13, 21]. The minimum difference between any pair is indeed 8 (21 - 13 = 8).
Time and Space Complexity
Time Complexity: O(n log n + n log(max_price))
The time complexity consists of two main parts:
- Sorting the price array takes
O(n log n)wherenis the length of the price list - Binary search runs in
O(log(max_price - min_price))iterations, wheremax_price - min_pricerepresents the search space - For each binary search iteration, the
checkfunction is called which takesO(n)time as it iterates through all prices once - Therefore, the binary search portion contributes
O(n * log(max_price - min_price)) - Since
max_price - min_price ≤ max_price, we can simplify this toO(n log(max_price)) - Overall:
O(n log n + n log(max_price))
Space Complexity: O(1) or O(log n)
- The algorithm uses only a constant amount of extra space for variables (
l,r,mid,cnt,pre,cur) - The sorting operation might use
O(log n)space if an in-place sorting algorithm like quicksort is used (for recursion stack) - If the sorting is done using Timsort (Python's default), it would use
O(n)auxiliary space in the worst case, but typicallyO(log n) - Excluding the sorting space, the algorithm itself uses
O(1)extra space
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
A common mistake is using the while left < right pattern with ceiling division instead of the standard first_true_index template.
Why this causes problems:
- Different binary search variants have subtle differences in boundary handling
- Mixing patterns leads to off-by-one errors or infinite loops
- The standard template provides consistency across all binary search problems
Solution: For maximization problems, use the first_true_index template with an inverted condition:
left, right = 0, max_value + 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if not feasible(mid): # Inverted: True when infeasible first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1 # Maximum feasible value
This maintains the canonical template structure while solving maximization problems.
2. Incorrect Greedy Validation Logic
Another pitfall is incorrectly implementing the validation function, particularly:
- Starting with
last_selected = 0instead oflast_selected = -min_diff - Using
<=instead of>=in the comparison - Forgetting to update
last_selectedafter selecting a candy
Why this matters:
- Starting with
last_selected = 0might incorrectly reject the first candy if its price is less thanmin_diff - The greedy approach only works because we're iterating through sorted prices
Solution:
Initialize last_selected = -min_diff or use a flag for the first candy:
def can_achieve_min_diff(min_diff):
count = 1 # Start with first candy selected
last_selected = price[0]
for i in range(1, len(price)):
if price[i] - last_selected >= min_diff:
last_selected = price[i]
count += 1
return count >= k
3. Forgetting to Sort the Array
The greedy approach only works on a sorted array. Without sorting, you cannot guarantee that selecting the next valid candy maintains the minimum difference constraint for all previously selected candies.
Solution: Always sort the price array before applying the binary search and greedy validation.
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
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
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
Want a Structured Path to Master System Design Too? Don’t Miss This!