🔗 🟡 2517. Maximum Tastiness of Candy Basket 2021

tags: Week Contest 325 二分搜尋(Binary Search) 排序(Sorting)

題意

給定一個正整數陣列 price\text{price} ,其中 price[i]\text{price}[i] 表示第 ii 種糖果的價格,另給定一個正整數 kk

kk不同 種類的糖果放入糖果籃中,糖果籃的 美味程度(tastiness) 是糖果籃中任意兩種糖果的價格差的最小值。

返回糖果籃的 最大 美味程度。

約束條件

  • 2kprice.length1052 \leq k \leq \text{price.length} \leq 10^5
  • 1price[i]1091 \leq \text{price}[i] \leq 10^9

首先確定思路,美味程度即最大的最小價格差,而要直接計算出最大的美味程度顯然是很困難的,但對於給定的美味程度 dd ,我們可以確定是否有 kk 顆糖果的價格差都 d\geq d,因此可以使用 二分搜尋(Binary Search) 來找到最大的最小價格差。

定義一個函數 check(d) 來檢查是否可以將 kk不同 種類的糖果放入糖果籃中,使得糖果籃的 美味程度(tastiness) 至少為 dd

  • 使用 cntcnt 來記錄放置糖果的數量,prepre 來記錄前一個糖果的價格。
  • 遍歷 price\text{price} 陣列中的每個元素 p\text{p}
    • 如果 ppred\text{p} - \text{pre} \geq d,代表可以放置糖果,則 cntcnt 加一,並更新 preprep\text{p}
  • 最後返回 cntkcnt \geq k,即是否可以放置 kk不同 種類的糖果。

這裡使用左閉右閉區間進行二分搜尋,令 left=0left = 0right=price[n1]price[0]right = \text{price}[n-1] - \text{price}[0],在 [left,right][left, right] 的範圍內進行二分搜尋,不斷調整左右範圍直到找到最大的最小價格差為止。

  • 根據定義的 check 函數,區間的左側為 True\text{True} ,而右側為 False\text{False}
  • 如果 check(mid)=True\text{check}(mid) = \text{True},則將左範圍擴大到 [mid+1,right][mid+1, right]
  • 如果 check(mid)=False\text{check}(mid) = \text{False},則將右範圍縮小到 [left,mid1][left, mid-1]
  • 當區間為空,即 left>rightleft > right 時,終止搜尋。

最終返回 rightright ,即最大的最小價格差。

複雜度分析

  • 時間複雜度:O(nlogn+nlogM)=O(nlog(nM))\mathcal{O}(n \log n + n \log M) = \mathcal{O}(n \log (nM)) ,其中 nn 為陣列 price\text{price} 的長度,MMprice\text{price} 的最大值。
    • 對陣列 price\text{price} 進行排序需要 O(nlogn)\mathcal{O}(n \log n) 時間。
    • 需要進行 logM\log M 次二分搜尋,每次搜尋需要遍歷 nn 個元素來檢查是否可以放置 kk不同 種類的糖果。
  • 空間複雜度:O(1)\mathcal{O}(1) ,忽略排序的空間複雜度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def maximumTastiness(self, price: List[int], k: int) -> int:
n = len(price)
price.sort()

def check(d: int) -> bool:
cnt = 1
pre = price[0]
for i in range(1, n):
if price[i] - pre >= d:
cnt += 1
pre = price[i]
return cnt >= k

left, right = 0, price[-1] - price[0]
while left <= right:
mid = (left + right) // 2
if check(mid):
left = mid + 1
else:
right = mid - 1
return right
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
int maximumTastiness(vector<int>& price, int k) {
int n = price.size();
sort(price.begin(), price.end());

auto check = [&](int d) {
int cnt = 1;
int pre = price[0];
for (int i = 1; i < n; ++i) {
if (price[i] - pre >= d) {
cnt++;
pre = price[i];
}
}
return cnt >= k;
};

int left = 0, right = price[n - 1] - price[0];
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
};

類題

最小化最大值

本質是二分答案求最小。

最大化最小值

本質是二分答案求最大。

題單來自 @灵茶山艾府


寫在最後

Cover photo is generated by @たろたろ, thanks for their work!

1552. Magnetic Force Between Two Balls 解題紀錄 完全相同,只是敘述上稍有不同。