leetcode
leetcode 2151 ~ 2200
礼盒的最大甜蜜度

礼盒的最大甜蜜度

难度:

标签:

题目描述

You are given an array of positive integers price where price[i] denotes the price of the ith candy and a positive integer k.

The store sells baskets of k distinct candies. The tastiness of a candy basket is the smallest absolute difference of the prices of any two candies in the basket.

Return the maximum tastiness of a candy basket.

 

Example 1:

Input: price = [13,5,1,8,21,2], k = 3
Output: 8
Explanation: Choose the candies with the prices [13,5,21].
The tastiness of the candy basket is: min(|13 - 5|, |13 - 21|, |5 - 21|) = min(8, 8, 16) = 8.
It can be proven that 8 is the maximum tastiness that can be achieved.

Example 2:

Input: price = [1,3,1], k = 2
Output: 2
Explanation: Choose the candies with the prices [1,3].
The tastiness of the candy basket is: min(|1 - 3|) = min(2) = 2.
It can be proven that 2 is the maximum tastiness that can be achieved.

Example 3:

Input: price = [7,7,7,7], k = 2
Output: 0
Explanation: Choosing any two distinct candies from the candies we have will result in a tastiness of 0.

 

Constraints:

  • 2 <= k <= price.length <= 105
  • 1 <= price[i] <= 109

代码结果

运行时间: 300 ms, 内存: 27.0 MB


/*
 * 思路:
 * 1. 首先对价格数组进行排序。
 * 2. 使用二分查找来找到最大甜蜜度,设定左右边界为0和价格数组中的最大差值。
 * 3. 对于每一个可能的甜蜜度,使用贪心算法检查是否能够选出k种糖果使得相邻糖果的差值大于等于该甜蜜度。
 */

import java.util.Arrays;
import java.util.stream.IntStream;

public class CandySweetnessStream {
    public int maximumSweetness(int[] price, int k) {
        Arrays.sort(price);
        int left = 0;
        int right = IntStream.of(price).max().orElse(0) - IntStream.of(price).min().orElse(0);
        int result = 0;

        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (canFormSweetBox(price, k, mid)) {
                result = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return result;
    }

    private boolean canFormSweetBox(int[] price, int k, int sweetness) {
        int count = 1;
        int prev = price[0];

        for (int i = 1; i < price.length; i++) {
            if (price[i] - prev >= sweetness) {
                count++;
                prev = price[i];
                if (count >= k) {
                    return true;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        CandySweetnessStream solution = new CandySweetnessStream();
        int[] price = {13, 5, 1, 8, 21, 2};
        int k = 3;
        System.out.println(solution.maximumSweetness(price, k)); // 输出: 8
    }
}

解释

方法:

该题解采用了排序与二分搜索的方法来求解最大甜蜜度。首先,将糖果价格排序,再通过二分搜索来确定最大的甜蜜度。二分搜索中,检查函数 'check(x)' 用于判断是否存在至少 'k' 种糖果,其价格差至少为 'x'。如果存在,说明甜蜜度可以更高;如果不存在,那么甜蜜度需要降低。通过不断调整 'x' 的值,直到找到最大的满足条件的 'x'。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么要在二分搜索中使用`(price[-1] - price[0]) // (k - 1) + 1`作为右边界r的初始值?这个表达式是如何计算得来的?
此表达式用于估算最大可能的甜蜜度。基于题目要求选择至少`k`种糖果,且他们的价格差须至少为`x`,最大的`x`出现在价格差最大的两端糖果之间等间隔地选取`k - 1`个糖果时。`(price[-1] - price[0])`给出了价格数组中最大和最小值的差异,将此差异等分为`k - 1`份,每份的大小即为理论上可能的最大`x`值。加`1`是为确保在二分搜索过程中覆盖所有可能的值。
🦆
在`check(x)`函数中,为什么选择从数组的最小价格开始,而不是从最大价格或其他价格开始?
从最小价格开始选择是为了最大化价格间隔。从最小值开始,可以保证在达到所需的糖果种类数量时,所选的糖果价格间隔尽可能大,从而可能达到更高的甜蜜度。如果从最大价格或其他价格开始,可能导致无法选择足够数量的糖果而错过最优解。
🦆
二分搜索终止条件为`while l < r`,为什么在`l == r`时停止搜索?此时的`l`或`r`是否代表一个有效的甜蜜度值?
在`l < r`的条件下终止搜索是因为,当`l`等于`r`时,搜索空间已经缩小到一个点,进一步的搜索将不再改变结果。此时的`l`(或`r`,因为此时它们相等)表示最后检查的一个潜在的甜蜜度值,通常是上一次检查`mid`值的上界-1,因为它是最后一个使`check(mid)`为`true`的值。
🦆
当`check(mid)`函数返回`true`时,为什么要将`l`设置为`mid + 1`,而不是直接使用`mid`作为可能的最大甜蜜度?
当`check(mid)`返回`true`时,意味着至少存在一个甜蜜度为`mid`的解决方案,但可能有更大的甜蜜度值也满足条件。因此,更新`l`为`mid + 1`是为了探索是否存在更大的满足条件的甜蜜度,从而确保找到最大可能的甜蜜度。如果直接使用`mid`,可能会错过更优的解。

相关问题