LeetCode日记( 第 N 个神奇数字)第四天

题目来自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类型

你可能感兴趣的:(leetcode,算法,职场和发展,java)