🔗 🟡 1552. Magnetic Force Between Two Balls 1920

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

題意

給定一個長度為 nn 的整數陣列 position\text{position} 和一個整數 mm,其中 position[i]\text{position}[i] 代表第 ii 個籃子的位置,現在需要把 mm 顆球放到這些籃子裡,使得任意兩球之間的 最小距離 最大。

若兩個球分別位於 xxyy,則它們之間的距離為 xy|x - y|

返回最大的最小距離。

約束條件

  • position.length=n\text{position.length} = n
  • 2n1052 \leq n \leq 10^5
  • 1position[i]1091 \leq \text{position}[i] \leq 10^9
  • 所有在 position\text{position} 中的整數都是 不同 的。
  • 2mposition.length2 \leq m \leq \text{position.length}

首先確定思路,要直接計算出最大的最小距離顯然是很困難的,但對於給定的距離 dd ,我們可以確定是否有 mm 顆球的距離都 d\geq d,因此可以使用 二分搜尋(Binary Search) 來找到最大的最小距離。

定義一個函數 check(d) 來檢查在待定距離 dd 內是否能放入 mm 顆球,使得任意兩球之間的距離至少為 dd

  • 使用 cntcnt 來記錄放置球的數量,prepre 來記錄前一個球的位置。
  • 遍歷 position\text{position} 陣列中的每個元素 pos\text{pos}
    • 如果 pospred\text{pos} - \text{pre} \geq d,代表可以放置球,則 cntcnt 加一,並更新 preprepos\text{pos}
  • 最後返回 cntmcnt \geq m,即是否可以放置 mm 顆球。

這裡使用左閉右閉區間進行二分搜尋,令 left=0left = 0right=position[n1]position[0]right = \text{position}[n-1] - \text{position}[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 為陣列 position\text{position} 的長度,MMposition\text{position} 的最大值。
    • 對陣列 position\text{position} 進行排序需要 O(nlogn)\mathcal{O}(n \log n) 時間。
    • 需要進行 logM\log M 次二分搜尋,每次搜尋需要遍歷 nn 個元素來檢查是否可以放置 mm 顆球。
  • 空間複雜度: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 maxDistance(self, position: List[int], m: int) -> int:
n = len(position)
position.sort()

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

left, right = 0, position[-1] - position[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 maxDistance(vector<int>& position, int m) {
int n = position.size();
sort(position.begin(), position.end());

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

int left = 0, right = position[n - 1] - position[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!

2517. Maximum Tastiness of Candy Basket 解題紀錄 完全相同,只是敘述上稍有不同。