leetcode周赛1552. 两球之间的磁力
在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力 最大。
已知两个球如果分别位于 x 和 y ,那么它们之间的磁力为 |x - y| 。
给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。
输入:position = [1,2,3,4,7], m = 3
输出:3
解释:将 3 个球分别放入位于 1,4 和 7 的三个篮子,两球间的磁力分别为 [3, 3, 6]。最小磁力为 3 。我们没办法让最小磁力大于 3 。
二分查找
leetcode上面有很多类似的(比如1482和lcp那个小张做题),这种题的特点是问最小的最大或者最大的最小。都是通过一个能取到的最大值开始二分,直至不满足条件。
拿本题来说,磁力的最大值是position的最后一个减掉第一个(其实除以球的个数会进一步缩小范围)。从这个最大值开始二分,查出来最后的答案,每次用当前的磁力值判断能不能构成这样的一种情况。比如说我们有三个球,position[1,5,10],假设当前算的磁力是4,check要判断这是不满足要求的。
check结束了,说一下左右边界的变化范围。因为是寻找最大化的,所以当存在一个满足条件后,需要让 left=mid+1 ,不存在令 right = mid - 1。
错误理解check
我们用一个check方法来检验当前磁力能否满足条件。
先看我的错误思路。
用hashmap存储position,key为position,value为1。check会检查是否存在一个数组的序列,其中任意一个数a,存在b,满足 a+distance==b。
private boolean check(HashMap<Integer,Integer> map,int distance,int m) {
int flag = 0;
Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer,Integer> entry = iterator.next();
flag = 1;
int prev = entry.getKey();
while (true) {
int n = prev + distance;
if (map.containsKey(n)) {
flag++;
prev = n;
} else {
break;
}
if (flag == m) {
return true;
}
}
}
return false;
}
这么做就错在等于上,因为我们是二分答案,可能不会直接存在这么一组样例满足恰好等于间隔的情况,这样就会由于二分的判定条件,令right=mid-1,从而错过正确答案(第二个样例就是)。正确的情况应该是存在两个数 a-b>=distance。
正确代码
class Solution {
public int maxDistance(int[] position, int m) {
Arrays.sort(position);
int len = position.length;
int left = 0,right = position[len - 1] - position[0];
int mid = 0;
while (left <= right) {
mid = (left + right) >>> 1;
boolean flag = check(position,mid,m);
if (flag) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left - 1;
}
//判断distance下整个数组是否存在满足条件的
//大于等于就行,不能严格等于
private boolean check(int[] arr,int distance,int m) {
int count = 0;
int i = 0;
for (int j = 1; j < arr.length; j++) {
if (arr[j] - arr[i] >= distance) {
count++;
if (m - 1== count) {
return true;
}
i = j;
}
}
return false;
}
}
leetcode 119