1552. 两球之间的磁力

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官方给的题解和一些大佬的题解,才明白怎么回事。这里总结一下吧

题意求最大化最小,类似这样的求最大化最小值、最小化最大值等都可以用二分搜索解决。

实现细节:
首先要找到二分搜索的边界,根据题意,要返回的是最小磁力,所以第一步要找到最小磁力的最小可能取值和最大可能取值。

对于最小可能取值,当然就是给定数组中距离最近的两个位置之间的磁力,所以对数组进行排序,并遍历数组找到相邻两个位置的最小距离。

对于最大可能取值,一共有m个球,所以有 m - 1 个间隔,最大的可能取值便是最平均的取值,所以根据给定数组最大值与最小值之差与间隔数的比值计算出平均距离,就是给定的最大可能取值。

这里给定简单证明,假设有 k 个间隔,给定数组规定的篮子间最大距离为 x,那么最小磁力的最大可能取值是 x / k,假设有某一可能取值 y 大于最大可能取值,那么所有距离都一定大于等于 y,此时假设 k 个间隔距离均为 y,总距离 ky > k * x / k = x,也大于给定的最大距离,所以不成立。

确定好了边界后,每次二分搜索时需要判断当前计算值是否满足条件,这里我们引入 check 函数,对当前计算出的最小磁力进行验证。验证过程使用贪心算法,遍历数组,若找到两位置之间距离大于等于最小磁力,则计数值加 1,最后只需要判断总计数值是否大于等于给定间隔数 m - 1 即可。

例如,示例 1 中,假设我们当前二分搜索计算出的距离为 2,那么我们遍历数组,假设第一个位置为 1,那么下一个找到的位置应该是 3,因为 3 - 1 >= 2,计数值加 1;再下面找到的是 7,因为 7 - 3 >= 2,计数值加 1。此时数组遍历完成,总计数值为 2,而给定间隔数 m - 1 = 2,满足条件,说明最小磁力为 2 是可以做到的。但如果我们当前计算出的距离为 4,那么第一个位置为 1,找到的第二个位置就只能是 7,数组遍历完成总计数值为 1,小于给定间隔数,说明最小磁力为 4 是不成立的。

在判断计算值满足条件与否之后,我们要对二分搜索边界进行转化,由于题目要求的是最大化的最小磁力,所以若当前计算出的最小磁力满足条件,我们要将左边界右移,去判断稍大一点的数值是否满足条件;若当前计算出的最小磁力不满足条件,我们要将右边界左移,判断稍小的数值是否满足条件。

代码

	//判断值t是否可以满足条件
    public static boolean check(int[] p,int m,int t){
        int count = 0;
        int n = p.length;
        int pre = p[0];
        for (int i=1;i<n;i++){
        	//如果当前的p[i]不满足if的条件,则i++,直到循环终止
            if(p[i]-pre>=t){
                count++;
                pre = p[i];
            }
        }
        return count>=m-1;
    }


    public static int maxDistance(int[] position, int m) {
		//首先对数组进行排序
        Arrays.sort(position);
        int n = position.length;
        int left  =Integer.MAX_VALUE;
        int right = (position[n-1]-position[0]); //这里最大边界可以这样设置,也可以按照上面的说明,用(position[n-1]-position[0])/(m-1)来设置
		
		//找到最小边界,当然也可以直接设置为1
        for(int i=0;i<position.length-1;i++){
            left = (position[i+1]-position[i])<left?(position[i+1]-position[i]):left;
        }
        int ans  = 0;
        //二分查找
        while (left<=right){

            int mid = (left+right)>>1;
            if(check(position,m,mid)){
                ans = mid;
                left = mid+1;
            }else {
                right = mid-1;
            }
        }
        return ans;
    }

总结

这种类型的题目第一次接触,确实很有收获
延伸一下:

类似题目(拷贝来的,等我慢慢刷完):
LeetCode 410. 分割数组的最大值(极小极大化 二分查找)
LeetCode 774. 最小化去加油站的最大距离(极小极大化 二分查找)
LeetCode 875. 爱吃香蕉的珂珂(二分查找)
LeetCode LCP 12. 小张刷题计划(二分查找)
LeetCode 1011. 在 D 天内送达包裹的能力(二分查找)
LeetCode 1102. 得分最高的路径(优先队列BFS/极大极小化 二分查找)
LeetCode 1062. 最长重复子串(二分查找)
LeetCode 5438. 制作 m 束花所需的最少天数(二分查找)
LeetCode 5489. 两球之间的磁力(极小极大化 二分查找)
参考:

  1. https://michael.blog.csdn.net/article/details/108035117
  2. https://leetcode-cn.com/problems/magnetic-force-between-two-balls/solution/c-er-fen-sou-suo-ying-gai-neng-gei-ni-jiang-ming-b/
  3. https://blog.csdn.net/qq_21201267/article/details/108035117
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值