题目 题源 代码 码源 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); } }