题目来自LeetCode编号878题
题目如下:
一个正整数如果能被 a
或 b
整除,那么它是神奇的。
给定三个整数 n
, a
, b
,返回第 n
个神奇的数字。因为答案可能很大,所以返回答案 对 109 + 7
取模 后的值。
示例 1:
输入:n = 1, a = 2, b = 3 输出:2
示例 2:
输入:n = 4, a = 2, b = 3 输出:6
先看代码:
class Solution {
static final int MOD = 1000000007;
public int nthMagicalNumber(int n, int a, int b)
{
long l=Math.min(a,b);
long r=(long)Math.min(a,b)*n;
int c=a*b/zz(a,b);//最小公倍数
while(l<=r)
{
long mid=(l+r)/2;
if(mid/a+mid/b-mid/c>=n)
{
r=mid-1;
}
else
{
l=mid+1;
}
}
return (int)((r+1)%MOD);
}
private static int zz(int a, int b) {
return b==0?a:zz(b,a%b);
}//求最小公约数
}
解题的思路如下:
对于我们要求的神奇数,它能被a整除或者能被b整除。
那么,神奇数是a或b的倍数,所以我们将 l(left)赋值为a与b中的最小值, r(right)赋值为a与b中的最小值的n倍(n是我们要求的第n个数)。
为什么要这样赋值呢?
我们将采用二分法查找范围内的数,那么这个范围我限定在(最小的数,最小的数的n倍)这个区间内。这个范围肯定是能得到比n大于或等于的个数的神奇数。
为什么呢,如果单看一个数假设是5,那么我要求n个神奇数,也就是能被5整除的数那么我就只需要将5*n就行了。现在回看我们现在是两个数,在5*n这个范围内可能第二个数远大于他,也可能在(5,5*n)这个范围内。所以这个范围内的神奇数的个数一定是大于或等于n的。
那么在范围内怎么解决重复的问题呢。
假设a=2,b=3,n=5,我们刚刚说了我们将范围定在(最小的数,最小的数的n倍)这个范围内。
那么范围就是(2,10);里面的神奇数我们完全可以列举出来2,3,4,6,8,10。不难看出我们要求的神奇数是8。
如果我们但看一个2他在这个范围内神奇数是2,4,6,8,10。
单看一个3,他在范围内神奇数是3,6,9。
很明显重复了一个6,这个6是他们的最小公倍数。
那么我们可以得到一个结论范围内的
神奇数的个数=a在范围内神奇数的个数 + b在范围内神奇数的个数-最小公倍数在范围内神奇数的个数。
接下来采用二分法查找。
对于范围内找到mid位置,如果mid位置的神奇数个数小于n那么我们就把l赋值为mid+1,
如果是大于等于r赋值为mid-1。
直到l>r退出循环
(二分法过程不明白的话可以将多个状况模拟一下)。
这时我们得到的r一定是比我们目标的位置小1的所以最后我们要+1最后与MOD取模。
记得要转换为int类型