“学习是劳动,是充满思想的劳动。”——乌申斯基
题目
丑数是可以被 a
或 b
或 c
整除的 正整数 。
给你四个整数:n
、a
、b
、c
,请你设计一个算法来找出第 n
个丑数。
难度:中等
分析
笔者的脑子🧠无法实现独立做出此题,故参考了提示和他人的思路,将题解记录于此,防止自己忘记╮(╯▽╰)╭。
我们可以观察到数越大,它包含的丑数就越多,因此可以考虑使用二分查找,用f(x)表示不超过x的丑数个数,可以把初始下限设置为a,b,c中的最小值,把初始上限设置为最小值*n。
对于f(x)的求解,直接遍历的耗时太长,需要考虑如何快速计算出结果。我们只考虑单个数a,那么x中包含的丑数即a的倍数个数为x/a,同理为b、c倍数的个数为x/b、x/c。如果我们只是将他们加起来,必然会有重复的数,因此我们需要考虑同时为ab、ac、bc、abc倍数的个数。计算方法参照韦恩图(即A+B+C-AB-AC-BC+ABC):
在C++的库函数中,没有直接求最小公倍数的函数,但是有求最大公因数的函数gcd(),因此可以使用gcd来求解最小公倍数(见代码)。
解答
class Solution {
public:
int nthUglyNumber(int n, int a, int b, int c) {
int left=min(min(a,b),c);
int right=left*n;
while (left<=right){
int mid=left+(right-left)/2;
int cur=f(mid,a,b,c);
if (cur<n){
left=mid+1;
}else{
right=mid-1;
}
}
return left;
}
long long f(long long x, long long a, long long b, long long c){
long long ad=x/a,bd=x/b,cd=x/c;
long long abd=x/lcm(a,b),acd=x/lcm(a,c),bcd=x/lcm(b,c);
long long abcd=x/lcm(a,lcm(b,c));
return ad+bd+cd-abd-acd-bcd+abcd;
}
long long lcm(long long a,long long b){
return (a/gcd(a,b))*b;
}
};