leetcode
leetcode 2201 ~ 2250
最小化两个数组中的最大值

最小化两个数组中的最大值

难度:

标签:

题目描述

We have two arrays arr1 and arr2 which are initially empty. You need to add positive integers to them such that they satisfy all the following conditions:

  • arr1 contains uniqueCnt1 distinct positive integers, each of which is not divisible by divisor1.
  • arr2 contains uniqueCnt2 distinct positive integers, each of which is not divisible by divisor2.
  • No integer is present in both arr1 and arr2.

Given divisor1, divisor2, uniqueCnt1, and uniqueCnt2, return the minimum possible maximum integer that can be present in either array.

 

Example 1:

Input: divisor1 = 2, divisor2 = 7, uniqueCnt1 = 1, uniqueCnt2 = 3
Output: 4
Explanation: 
We can distribute the first 4 natural numbers into arr1 and arr2.
arr1 = [1] and arr2 = [2,3,4].
We can see that both arrays satisfy all the conditions.
Since the maximum value is 4, we return it.

Example 2:

Input: divisor1 = 3, divisor2 = 5, uniqueCnt1 = 2, uniqueCnt2 = 1
Output: 3
Explanation: 
Here arr1 = [1,2], and arr2 = [3] satisfy all conditions.
Since the maximum value is 3, we return it.

Example 3:

Input: divisor1 = 2, divisor2 = 4, uniqueCnt1 = 8, uniqueCnt2 = 2
Output: 15
Explanation: 
Here, the final possible arrays can be arr1 = [1,3,5,7,9,11,13,15], and arr2 = [2,6].
It can be shown that it is not possible to obtain a lower maximum satisfying all conditions. 

 

Constraints:

  • 2 <= divisor1, divisor2 <= 105
  • 1 <= uniqueCnt1, uniqueCnt2 < 109
  • 2 <= uniqueCnt1 + uniqueCnt2 <= 109

代码结果

运行时间: 22 ms, 内存: 16.1 MB


/*
题目思路:
1. 使用Stream API来简化遍历和过滤的过程。
2. 从1开始生成无限的正整数流。
3. 对于每个数字,检查它是否可以添加到arr1或arr2中。
4. 使用Stream API的filter方法来过滤掉不符合条件的数字。
5. 使用limit方法来限制最终结果的大小。
6. 计算出两个数组中的最大值。
*/
import java.util.stream.IntStream;
public class Solution {
    public int findMinimumMaxValue(int divisor1, int divisor2, int uniqueCnt1, int uniqueCnt2) {
        int[] count = {0, 0};
        return IntStream.iterate(1, i -> i + 1)
                .filter(i -> (i % divisor1 != 0 && count[0] < uniqueCnt1) || (i % divisor2 != 0 && count[1] < uniqueCnt2))
                .limit(uniqueCnt1 + uniqueCnt2)
                .peek(i -> {
                    if (i % divisor1 != 0 && count[0] < uniqueCnt1) count[0]++;
                    else if (i % divisor2 != 0 && count[1] < uniqueCnt2) count[1]++;
                })
                .max()
                .getAsInt();
    }
}

解释

方法:

此题的解题思路采用了二分查找的方法。首先定义一个有效性函数 valid,用于判断对于给定的最大值 mid,是否能够满足两个数组的条件。函数 valid 中,首先计算不能被 divisor1 和 divisor2 整除的正整数数量 va 和 vb。然后计算能同时被 divisor1 和 divisor2 整除的正整数数量 shared。最后判断 va 和 vb 是否分别大于等于 uniqueCnt1 和 uniqueCnt2,以及 va 和 vb 减去 shared 是否大于等于 uniqueCnt1 和 uniqueCnt2 的总和。二分查找的过程中,根据 valid 函数的返回值来调整搜索范围,最终找到满足条件的最小的最大值。

时间复杂度:

O(logN)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在二分查找的上限设置为2*(uniqueCnt1 + uniqueCnt2)+1,这个上限的计算方法有什么特别的依据吗?
这个上限的设置是基于保守估计的考虑。考虑到问题中要求的是数组中不能被 divisor1 和 divisor2 整除的最小最大元素数量,最坏情况下,每个 divisor 可能分别占据一个数,剩余的数都是不可整除的。因此,为了保证有足够的数来满足 uniqueCnt1 和 uniqueCnt2 的需求,我们可以估计数值至少需要是 uniqueCnt1 和 uniqueCnt2 两倍的总和,再加一以保证可以覆盖所有可能的边界情况。这是一个保守的估计,用以确保在最坏的情形下,我们仍有足够的数值范围来进行搜索。
🦆
在计算最小公倍数LCM时采用的是`divisor1*divisor2//gcd(divisor1, divisor2)`,这种方法在所有情况下都准确无误吗,特别是在较大的输入值时是否会有整数溢出的风险?
该方法在理论上是正确的,因为两个数的乘积除以它们的最大公约数确实是它们的最小公倍数(LCM)。然而,在实际应用中,当 divisor1 和 divisor2 非常大时,直接计算 `divisor1*divisor2` 可能会导致整数溢出,尤其是在某些编程语言中,这可能会导致运算结果不准确。为了避免这种情况,可以先除以 gcd,再与另一个除数相乘,即 `lcm = (divisor1 // gcd(divisor1, divisor2)) * divisor2`。这样做可以减少乘法运算中的数值大小,从而减少溢出的风险。
🦆
函数`valid`中`shared`的计算方法是`mid - mid//divisor1 - mid//divisor2 + mid//lcm`,这种计算方式的目的是什么,能否详细解释其逻辑?
这种计算方法基于容斥原理。函数中的 `shared` 是用来计算同时被 divisor1 和 divisor2 整除的数的数量。首先,`mid // divisor1` 给出了小于或等于 mid 的数中能被 divisor1 整除的数的数量,`mid // divisor2` 给出了能被 divisor2 整除的数量。这两个集合相加时,同时被 divisor1 和 divisor2 整除的数被重复计算了一次,因此需要减去这部分的重复计数,即 `mid // lcm`,这里 lcm 是 divisor1 和 divisor2 的最小公倍数。因此,`mid - mid//divisor1 - mid//divisor2 + mid//lcm` 正确计算了不被 divisor1 或 divisor2 整除的数的数量。

相关问题