丑数 III

题目

题源
在这里插入图片描述

代码

码源

class Solution {
    public int nthUglyNumber(int n, int a, int b, int c) {
        if (a == 1 || b == 1 || c == 1) return n;

        long ab =  lcm(a, b);
        long ac =  lcm(a, c);
        long bc =  lcm(b, c);
        long abc =  lcm(ab, c);

        long left = Math.min(Math.min(a,b),c);
        long right = (long)Math.pow(left,n);// 上边界是这个最小者的n倍

        while (left <= right) {
            long mid = left + (right - left) / 2;
            long count = mid / a + mid / b + mid / c - mid / ab - mid / ac - mid / bc + mid / abc;
            if (count == n) {
                left = mid;
                break;
            } else if (count > n) {
                right = mid - 1;
                
            } else {
                left = mid + 1;
            }
        }
        // 比如第n个丑数是X,那么[X,X + min(a,b,c))这个半开区间内的所有数都同时包含n个丑数因子,我们通过二分法得到的答案也随机分布于这个区间中。而实际上我们只需要得到该区间的左端即可。处理方法很简单:假设我们得到的临时答案是K(K∈[X,X + min(a,b,c))),那么K - min(K%a,K%b,K%c) = X.也就是只需要把临时答案减去其与a、b、c三者中取余的最小值即可!
        return (int)(left - Math.min(Math.min(left % a,left % b),left % c));
    }
    // 最小公倍数
    private long lcm(long a, long b) {
        // 最小公倍数等于两个数相乘除以最大公约数
        return a * b / gcd(a, b);
    }
    // 最大公约数
    private long gcd (long a, long b) {
        if (a == 0) return b;
        return gcd(b % a, a);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值