Open azl397985856 opened 2 years ago
class Solution {
public double solve(int[] nums) {
Arrays.sort(nums);
int streetLength = nums[nums.length - 1] - nums[0];
int low = 0, high = streetLength / 3 + 1;
while (low + 1 < high) {
int mid = low + (high - low) / 2;
if (isPossible(nums, mid, 3)) {
high = mid;
} else {
low = mid;
}
}
if (isPossible(nums, low, 3)) {
return low / 2D;
}
return high / 2D;
}
private boolean isPossible(int[] nums, int radius, int lightNumber) {
int lightRadius = -1;
int currentLightNum = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > lightRadius) {
currentLightNum++;
lightRadius = nums[i] + radius;
}
if (currentLightNum > lightNumber) {
return false;
}
}
return true;
}
}
思路 由于我们想要找到满足条件的最小值,因此可直接套用最左二分模板
class Solution { public double solve(int[] nums) { Arrays.sort(nums); int streetLength = nums[nums.length - 1] - nums[0]; int low = 0, high = streetLength / 3 + 1; while (low + 1 < high) { int mid = low + (high - low) / 2; if (isPossible(nums, mid, 3)) { high = mid; } else { low = mid; } } if (isPossible(nums, low, 3)) { return low / 2D; } return high / 2D; }
private boolean isPossible(int[] nums, int radius, int lightNumber) {
int lightRadius = -1;
int currentLightNum = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > lightRadius) {
currentLightNum++;
lightRadius = nums[i] + radius;
}
if (currentLightNum > lightNumber) {
return false;
}
}
return true;
}
}
二分
public class MinimumLightRadius {
public double solve(int[] nums) {
Arrays.sort(nums);
int min = 0, max = (nums[nums.length-1]-nums[0])/3 + 1;
while(min +1 < max) {
int mid = min + (max-min)/2;
if(isPossible(nums, mid, 3))
max = mid;
else
min = mid;
}
if(isPossible(nums, min,3))
return min/2.0;
return max/2.0;
}
private boolean isPossible(int[] nums, int radius, int lightNumber) {
int lightRadius = -1;
int currentLightNum = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > lightRadius) {
currentLightNum++;
lightRadius = nums[i] + radius;
}
if (currentLightNum > lightNumber) {
return false;
}
}
return true;
}
}
借鉴官方解法
import java.util.*;
class Solution {
public int solve(int[] nums) {
List<Integer> list = new ArrayList();
int ret = 0;
for (int i = 0; i < nums.length; i++) {
int where = find(list, nums[i] * 3);
ret += list.size() - where;
list.add(find(list, nums[i]), nums[i]);
}
return ret;
}
public int find(List<Integer> list, int find) {
int l = 0;
int r = list.size();
while (l < r) {
int mid = l + (r - l) / 2;
if (list.get(mid) > find) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
}class Solution {
solve(nums: Array<number>): number {
if (!nums.length) return 0;
const n = nums.length;
nums.sort((a, b) => a - b);
let left = 0, right = nums[n - 1] - nums[0];
while (left <= right) {
let mid = (left + right) >> 1;
if (isCover(nums, mid)) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left / 2;
}
}
function isCover(nums: Array<number>, radius: number): boolean {
let left = nums[0], right = left + radius;
for (let i = 0; i < 3; i++) {
let j = searchRight(nums, right);
if (j >= nums.length) return true;
left = nums[j], right = left + radius;
}
return false;
}
function searchRight(nums: number[], target: number, left: number = 0, right: number = nums.length): number {
if (left < 0) throw ('left must be non-negative');
while (left < right) {
let mid = (left + right) >> 1;
if (target < nums[mid]) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
export { Solution }
class Solution:
def solve(self, nums):
light_num = 3
nums.sort()
n = len(nums)
def possible(diameter):
begin = nums[0]
end = begin + diameter
for i in range(3):
idx = bisect_right(nums, end)
if idx >= n:
return True
end = nums[idx] + diameter
return False
l = 0
r = nums[-1]-nums[0]#如果只有一盏灯所需最大直径
while l <= r:
mid = (l + r) //2
if possible(mid):#进一步缩短
r = mid - 1
else:
l = mid + 1
return l / 2
今天又抄作业了。算是击到了自己的一个痛点。
一般使用二分搜索,总是想着找一个已有的值。例如,在本题中,就是想着找 x。 但实际上应该把思路拓宽。题解里就是使用二分搜索来逼近直径。与其叫“搜索”不如叫“求值”。
简单分析之后可以发现,直径的范围一定是小于两端的房子的(取值空间)。 又,能够照亮一个房子的极值是房子正好在灯光的尽头(判定条件)。 有了这两个点,我们要做的就是代入直径,去看灯光能否将所有的房子覆盖。
代码中,取 mid 的时候使用 int
就可以的理由是所有房子的坐标都是 int
。
如果房子数小于等于 3,则把灯分配给每个房子即可(半径为 0,灯插家门口得了);
当房子数大于 3 的时候,再极端的情况,灯的半径都不会小于 0.5,且会以 0.5 的倍数而变化。
继而,即使 mid 为 int,当我们逼近到最后的只有两个候选值的时候,只有其中一个能将所有房子覆盖,即那个更大的值。
CPP
class Solution {
public:
double solve(vector<int> &nums) {
if (nums.size() <= 3) return 0; // every house can have its own light
sort(nums.begin(), nums.end());
int l = 0;
int r = nums[nums.size()-1] - nums[0];
while (l <= r) {
int mid = l + (r - l) / 2;
if (all_covered(nums, mid)) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return (double) l / 2;
}
private:
bool all_covered(vector<int> &nums, int r)
{
int begin = nums[0];
int range = begin + r;
int n = 0;
for (int i = 0; i < 3; i++) {
if (nums[n] <= range) n++;
if (n >= nums.size()) return true;
begin = nums[n];
range = begin + r;
}
return false;
}
}
复杂度分析
抄答案
public double solve(int[] nums) {
Arrays.sort(nums);
int streetLength = nums[nums.length - 1] - nums[0];
int low = 0, high = streetLength / 3 + 1;
while (low + 1 < high) {
int mid = low + (high - low) / 2;
if (isPossible(nums, mid, 3)) {
high = mid;
} else {
low = mid;
}
}
if (isPossible(nums, low, 3)) {
return low / 2D;
}
return high / 2D;
}
private boolean isPossible(int[] nums, int radius, int lightNumber) {
int lightRadius = -1;
int currentLightNum = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > lightRadius) {
currentLightNum++;
lightRadius = nums[i] + radius;
}
if (currentLightNum > lightNumber) {
return false;
}
}
return true;
}
抄写答案
class Solution: def solve(self, nums): nums.sort() N = len(nums) if N <= 3: return 0 LIGHTS = 3
def possible(diameter):
start = nums[0]
end = start + diameter
for i in range(LIGHTS):
idx = bisect_right(nums, end)
if idx >= N:
return True
start = nums[idx]
end = start + diameter
return False
l, r = 0, nums[-1] - nums[0]
while l <= r:
mid = (l + r) // 2
if possible(mid):
r = mid - 1
else:
l = mid + 1
return l / 2
class Solution:
def solve(self, nums):
nums.sort()
N = len(nums)
if N <= 3:
return 0
LIGHTS = 3
# 这里使用的是直径,因此最终返回需要除以 2
def possible(diameter):
start = nums[0]
end = start + diameter
for i in range(LIGHTS):
idx = bisect_right(nums, end)
if idx >= N:
return True
start = nums[idx]
end = start + diameter
return False
l, r = 0, nums[-1] - nums[0]
while l <= r:
mid = (l + r) // 2
if possible(mid):
r = mid - 1
else:
l = mid + 1
return l / 2
看官方题解稍微理解了个大概。。。
bool isPossible(vector<int>& nums, int mid) {
int start = nums[0];
int end = start + mid;
for (int i = 0; i < 3; i++) {
int index = 0;
while (nums[index] <= end) index++;
if (index >= nums.size()) return true;
start = nums[index];
end = start + mid;
}
return false;
}
double solve(vector<int>& nums) {
if (nums.size() <= 3) return 0;
sort(nums.begin(), nums.end());
int l = 0;
int r = nums[nums.size() - 1] - nums[0];
while (l <= r) {
int mid = l + (r - l) / 2;
if (isPossible(nums, mid)) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return (double)l / 2;
}
抄作业
class Solution {
public int[] sortArray(int[] nums) {
heapSort(nums);
return nums;
}
public void heapSort(int[] nums) {
int len = nums.length - 1;
buildMaxHeap(nums, len);
for (int i = len; i >= 1; --i) {
swap(nums, i, 0);
len -= 1;
maxHeapify(nums, 0, len);
}
}
public void buildMaxHeap(int[] nums, int len) {
for (int i = len / 2; i >= 0; --i) {
maxHeapify(nums, i, len);
}
}
public void maxHeapify(int[] nums, int i, int len) {
for (; (i << 1) + 1 <= len;) {
int lson = (i << 1) + 1;
int rson = (i << 1) + 2;
int large;
if (lson <= len && nums[lson] > nums[i]) {
large = lson;
} else {
large = i;
}
if (rson <= len && nums[rson] > nums[large]) {
large = rson;
}
if (large != i) {
swap(nums, i, large);
i = large;
} else {
break;
}
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
func solve(nums []int) int{ sort.Ints(nums) n := len(nums) if n <= 3 { return 0 } left := 0 right := (nums[n-1] - nums[0]) / 3 for left <= right { mid := left + (right-left)/2 if canCoverAll(mid,nums){ right = mid - 1 }else{ left = mid + 1 } } return left / 2.0 }
func canCoverAll(d int,nums []int) bool{ target := nums[0]+d n := len(nums) left,right := 0,n for left <= right{ mid := (left+right)/2 if nums[mid] > target{ right = mid - 1 }else{ left = mid + 1 } } leftBoard := left target = nums[n-1]-d for left <= right{ mid := left + (right-left)/2 if nums[mid] < target{ left = mid+1 }else{ right = mid-1 } } rightBoard := right if (rightBoard<0||nums[rightBoard]-nums[leftBoard] <= d){ return true }else{ return false } }
function solve(nums) {
console.log(nums);
if (nums.length < 3) return 0
nums.sort((a, b) => a - b)
let l = 0, r = nums[nums.length - 1]
while (l <= r) {
const m = l + ((r - l) >>> 1)
if (find(m, nums)) r = m - 1
else l = m + 1
}
if (l > nums[nums.length - 1] || !find(l, nums)) return -1
return l / 2
}
function find(mid, nums) {
let range = nums[0] + mid, ligths = 0
for (let i = 0; i < nums.length; i++) {
if (nums[i] > range) {
ligths += 1
range = nums[i] + mid
}
}
return ligths <= 2
}
console.log(solve([1,2,95,96,97,100]))
https://binarysearch.com/problems/Minimum-Light-Radius
Question 791 of 1031
++
You are given a list of integers nums
representing coordinates of houses on a 1-dimensional line. You have 3
street lights that you can put anywhere on the coordinate line and a light at coordinate x
lights up houses in [x - r, x + r]
, inclusive. Return the smallest r
required such that we can place the 3
lights and all the houses are lit up.
Constraints
class Solution:
def solve(self, nums):
# Edge cases
if len(nums)<=3:
return 0
# Binary search
nums.sort()
low = 0
high = (nums[-1]-nums[0]) / 3 # 直径最大值
# 是否可以覆盖
def can(num):
index = 0
cover = nums[0]+num
light = 1
while index<len(nums):
if nums[index] <= cover:
# 还在被照亮的范围
index += 1
else:
#当前点已经无法被照亮
if light == 3:
# 灯的数量已经到达3了,直接返回false
return False
else:
# 安放新的灯,该灯最左边能照亮到 nums[index]
cover = nums[index]+num
light += 1
# 遍历完所有的点
return True
while (low<high):
middle = (low+high)//2
if can(middle):
high = middle
else:
low = middle+1
return low/2
### C++
```C++
class Solution:
def solve(self, nums):
nums.sort()
N = len(nums)
if N <= 3:
return 0
LIGHTS = 3
def can(diameter):
start = nums[0]
end = start + diameter
for i in range(LIGHTS):
idx = bisect_right(nums, end)
if idx >= N:
return True
start = nums[idx]
end = start + diameter
return False
lo = -1
hi = nums[-1] - nums[0]
# Invariant: the answer lies in the interval (lo, hi]
while lo < hi - 1:
mid = (lo + hi) // 2
if can(mid):
hi = mid
else:
lo = mid
return hi / 2
class Solution:
def solve(self, nums):
nums.sort()
N = len(nums)
if N <= 3:
return 0
LIGHTS = 3
# 这里使用的是直径,因此最终返回需要除以 2
def possible(diameter):
start = nums[0]
end = start + diameter
for i in range(LIGHTS):
idx = bisect_right(nums, end)
if idx >= N:
return True
start = nums[idx]
end = start + diameter
return False
l, r = 0, nums[-1] - nums[0]
while l <= r:
mid = (l + r) // 2
if possible(mid):
r = mid - 1
else:
l = mid + 1
return l / 2
class Solution {
public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int radius = 0;
int i = 0;
for (int house : houses) {
while (i < heaters.length && heaters[i] < house) {
i++;
}
if (i == 0)
radius = Math.max(radius, heaters[i] - house);
else if (i == heaters.length)
return Math.max(radius, houses[houses.length-1] - heaters[heaters.length-1]);
else
radius = Math.max(radius, Math.min(heaters[i] - house, house - heaters[i - 1]));
}
return radius;
}
}
class Solution:
def solve(self, nums):
nums.sort()
N = len(nums)
if N <= 3:
return 0
LIGHTS = 3
# 这里使用的是直径,因此最终返回需要除以 2
def possible(diameter):
start = nums[0]
end = start + diameter
for i in range(LIGHTS):
idx = bisect_right(nums, end)
if idx >= N:
return True
start = nums[idx]
end = start + diameter
return False
l, r = 0, nums[-1] - nums[0]
while l <= r:
mid = (l + r) // 2
if possible(mid):
r = mid - 1
else:
l = mid + 1
return l / 2
二分法
class Solution:
def solve(self, nums):
nums.sort()
def check(r):
start=nums[0]
for i in range(3):
end=start+r
idx=bisect_right(nums,end)
if idx>=len(nums):
return True
start=nums[idx]
return False
left=0
right=nums[-1]-nums[0]
while left<=right:
mid=(left+right)//2
if check(mid):
right=mid-1
else:
left=mid+1
return left/2
嵌套二分,开始题意没读懂,nums里面应该是房子坐标吧 class Solution: def solve(self, nums): nums.sort() N = len(nums) if N <= 3: return 0 LIGHTS = 3
def possible(diameter):
start = nums[0]
end = start + diameter
for i in range(LIGHTS):
idx = bisect_right(nums, end)
if idx >= N:
return True
start = nums[idx]
end = start + diameter
return False
l, r = 0, nums[-1] - nums[0]
while l <= r:
mid = (l + r) // 2
if possible(mid):
r = mid - 1
else:
l = mid + 1
return l / 2
class Solution {
public double solve(int[] nums) {
Arrays.sort(nums);
// diameter min and max
int dMin = 0;
int dMax = nums[nums.length - 1] - nums[0];
int ans = 0;
while (dMin < dMax) {
int mid = (dMin + dMax) / 2;
if (isValidDiameter(nums, mid)) {
dMax = mid;
} else {
dMin = mid + 1;
}
}
return (double)dMax / 2;
}
private boolean isValidDiameter(int[] nums, int d) {
int start = nums[0];
int end = start + d;
int count = 1;
for (int num : nums) {
if (num > end) {
if (count == 3) {
return false;
} else {
start = num;
end = start + d;
count++;
}
}
}
return true;
}
}
不太理解. class Solution { public double solve(int[] nums) { Arrays.sort(nums); int n = nums.length; int left = 0, right = (nums[n - 1] - nums[0]) / 3; while(left <= right){ int mid = (left + right) / 2; if(allCovered(mid, nums)){ right = mid - 1; }else{ left = mid + 1; } } return left / 2.0; }
private boolean allCovered(int d, int[] nums){
int target = nums[0] + d, n = nums.length;
int left = 0, right = n - 1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] > target){
right = mid - 1;
}else{
left = mid + 1;
}
}
int leftBorder = left;
target = nums[n - 1] - d;
left = 0;
right = n - 1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] < target){
left = mid + 1;
}else{
right = mid - 1;
}
}
int rightBorder = right;
if(rightBorder < 0 || nums[rightBorder] - nums[leftBorder] <= d){
return true;
}else{
return false;
}
}
}
题目连接: Minimum Light Radiushttps://binarysearch.com/problems/Minimum-Light-Radius
关键点,但还没搞明白为何:每个house的坐标为int整数型,每个灯的覆盖区域也须为int整数才是最优的
二分思想:
class Solution:
def solve(self, nums):
nums.sort()
n = len(nums)
if n <= 3: # <= 3个房屋
return 0
def check(diameter):
idx = 0
for i in range(3):
end = nums[idx] + diameter # 灯最右端的覆盖范围
idx = bisect.bisect_right(nums, end) # 找出等覆盖范围内的house数目
if idx >= n: # 灯能够覆盖到全部的house
return True
return False
left, right = 0, nums[-1] - nums[0]
while left < right:
mid = (left + right) // 2
if check(mid): # 当前diameter取值可以覆盖3个house
right = mid # 则diameter可适当取小一些再试探
else: # 当前diameter取值过小,则可增大diameter继续试探
left = mid + 1
return left/2 # 最终的left=right即为最终的diameter,除以2即为所要求的最小radius
复杂度分析
class Solution {
public int[] sortArray(int[] nums) {
heapSort(nums);
return nums;
}
public void heapSort(int[] nums) {
int len = nums.length - 1;
buildMaxHeap(nums, len);
for (int i = len; i >= 1; --i) {
swap(nums, i, 0);
len -= 1;
maxHeapify(nums, 0, len);
}
}
public void buildMaxHeap(int[] nums, int len) {
for (int i = len / 2; i >= 0; --i) {
maxHeapify(nums, i, len);
}
}
public void maxHeapify(int[] nums, int i, int len) {
for (; (i << 1) + 1 <= len;) {
int lson = (i << 1) + 1;
int rson = (i << 1) + 2;
int large;
if (lson <= len && nums[lson] > nums[i]) {
large = lson;
} else {
large = i;
}
if (rson <= len && nums[rson] > nums[large]) {
large = rson;
}
if (large != i) {
swap(nums, i, large);
i = large;
} else {
break;
}
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
看不太懂
class Solution {
public int flipgame(int[] fronts, int[] backs) {
Set<Integer> same = new HashSet();
for (int i = 0; i < fronts.length; ++i)
if (fronts[i] == backs[i])
same.add(fronts[i]);
int ans = 9999;
for (int x : fronts)
if (!same.contains(x))
ans = Math.min(ans, x);
for (int x : backs)
if (!same.contains(x))
ans = Math.min(ans, x);
return ans % 9999;
}
}
O(NLongN) O(N)
int need(vector<int>& nums, double r) {
int ans = 0;
for (int i = 0; i < nums.size();) {
ans++;
int right = nums[i] + 2 * r;
i = upper_bound(nums.begin(), nums.end(), right) - nums.begin();
}
return ans;
}
double solve(vector<int>& nums) {
if (nums.size() <= 3) return 0;
sort(nums.begin(), nums.end());
int N = nums.size();
double l = 0, r = nums.back() - nums.front();
for (int i = 0; i < 32; i++) {
double mid = (l + r) / 2;
if (need(nums, mid) <= 3)
r = mid;
else
l = mid;
}
return r;
}
// 补打卡
bool help(vector<int>& nums, int mid) {
int start = nums[0];
int end = start + mid;
for (int i = 0; i < 3; i++) {
int idx = 0;
while (nums[idx] <= end) idx++;
if (idx >= nums.size()) return true;
start = nums[idx];
end = start + mid;
}
return false;
}
double solve(vector<int>& nums) {
if (nums.size() < 3) return 0;
sort(nums.begin(), nums.end());
// left right
int l = 0;
int r = nums[nums.size()-1] - nums[0];
while (l <= r) {
int mid = l + (r - l) / 2;
if (help(nums, mid)) {
r = mid - 1;
}
else {
l = mid + 1;
}
}
return (double) l / 2;
}
class Solution {
public:
// 本体可以抽象成寻找里热源最远距离的点
int findRadius(vector<int>& houses, vector<int>& heaters) {
sort(houses.begin(),houses.end());
sort(heaters.begin(),heaters.end());
int ans = 0;
for (auto const & val:houses){
int r = needRadius(val,heaters);
ans = max(r,ans);
}
return ans;
}
int needRadius(int val,vector<int>& heater){
if(val>=heater.back())return val-heater.back();
if(val<=heater.front())return heater.front()-val;
int left = 0;
int right = heater.size()-1;
while (true){
int mid = long (left+right)/2;
if(heater[mid]<val){
left = mid+1;
}else if (heater[mid]==val){
return 0;
}else{
if(heater[mid-1]<=val){
return min(val-heater[mid-1],heater[mid]-val);
}else{
right = mid-1;
}
}
}
return 0;
}
};
796. Minimum Light Radius
入选理由
暂无
题目地址
https://binarysearch.com/problems/Minimum-Light-Radius
前置知识
题目描述
Constraints
n ≤ 100,000 where n is the length of nums Example 1 Input nums = [3, 4, 5, 6] Output 0.5 Explanation If we place the lamps on 3.5, 4.5 and 5.5 then with r = 0.5 we can light up all 4 houses.