LeetCode·每日一题·878.第N个神奇数字·二分查找

链接:https://leetcode.cn/problems/nth-magical-number/solutions/1984767/er-fen-cao-zuo-zhu-shi-chao-ji-xiang-xi-5nfti/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 

题目

 

思路

按照题目意思直接暴力枚举,a和b的所有倍数,然后取 第 n 个 倍数即可,但是 n 有 10^9 次大小,肯定是会超的,所以需要优化时间。

从两个方面进行优化:

  • a 和 b 的所有倍数是不是都需要枚举出来?
    • 其实是不需要的,我们只需要第 n 个数据,其中的我们并不关心,那么我们可以 对 a 和 b 进行对应的乘积,a * n 则存在 n+1 个有效元素, b * n 也存在 n+1 个元素
      • 那么现在是不是存在 2n + 2 个 有效元素呢?
        • 并不是 这些数据中还存在 a 和 b 的公倍数是 重复计算的, 比如 a = 2, b = 3, 那么 6 就是a和b的计算进去的有效元素,需要去掉,当然在实际计算过程中我们只需要 n + 1 个有效元素即可,所有取 a,b中最小的元素 * n。当然取全部也没问题,只是计算量大小问题
  • 寻找第 n 个 倍数 的时候 是否 需要从头开始检索每一个倍数元素?
    • 我们现在已经知道了 前 2n + 2 个元素了,如果从头开始寻找第 n个元素 肯定是不行的,那么有没有比较快的查找算法呢?
      • 二分查找 二分操作边界情况分析
      • 设答案为 x,循环结束时,≤x 的神奇数字有 n 个,而 ≤x−1 的神奇数字不足 n 个。只有当 x 是一个神奇数字时,才会出现这种情况。

代码

#define MIN(a, b) ((a) < (b) ? (a) : (b))


int gcd(int a, int b) {
    return b != 0 ? gcd(b, a % b) : a;
}
int lcm(int a, int b) {//辗转相除法
    return a * b / gcd(a, b);
}

int nthMagicalNumber(int n, int a, int b) {
    const int MOD = 1e9 + 7;
    long left = MIN(a, b);
    long right = (long) n * MIN(a, b);//左右边界
    int c = lcm(a, b);
    while (left <= right) {//二分查找
        long mid = (left + right) / 2;
        long cnt = mid / a + mid / b - mid / c;//当前有效元素的个数
        if (cnt >= n) {
            right = mid - 1;// 范围缩小到 [left, mid]
        } else {
            left = mid + 1;// 范围缩小到 [mid, right]
        }
    }
    return (right + 1) % MOD;
}


作者:小迅
链接:https://leetcode.cn/problems/nth-magical-number/solutions/1984767/er-fen-cao-zuo-zhu-shi-chao-ji-xiang-xi-5nfti/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
    const long MOD = 1e9 + 7;
public:
    int nthMagicalNumber(int n, int a, int b) {
        long lcm = std::lcm(a, b);
        long left = 0, right = (long) min(a, b) * n; //左闭右闭 [left, right]
        while (left <= right) { // 开区间不为空
            long mid = left + (right - left) / 2;
            if (mid / a + mid / b - mid / lcm >= n)
                right = mid - 1; // 范围缩小到 [left, mid]
            else
                left = mid + 1; // 范围缩小到 [mid, right]
        }
        return (right + 1) % MOD;
    }
};


作者:小迅
链接:https://leetcode.cn/problems/nth-magical-number/solutions/1984767/er-fen-cao-zuo-zhu-shi-chao-ji-xiang-xi-5nfti/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值