原题如下
https://leetcode-cn.com/problems/ugly-number-iii/
题解
首先注意审题,题目说的是,可以被 a 或 b 或 c 整除的 正整数,只要至少被abc之一整除即可。
方法一 暴力
暴力法也就是从1开始判断,一个数是不是至少能被三个数之一整除,直到找到第n个为止。这样的方法效率极低,而且只能通过3个示例。
class Solution {
public int nthUglyNumber(int n, int a, int b, int c) {
int i=0;
int ans=1;
while(i<n){
if(ans%a==0||ans%b==0||ans%c==0){
i++;
}
ans++;
}return ans-1;
}
}
//3 / 50 个通过测试用例 状态:超出时间限制
//最后执行的输入:1000000000 2 217983653 336916467
方法二 二分求交集法
说到用求交集的方法,因为对于某个比较大的数,我们需要首先找到在小于这个数的范围内,有多少能被a、b或者c整除的的数字也就是能被三个数整除的数的集合取个并集。对于有交集的三个集合取总并集,有这个公式,A∪B∪C=A+B+C-A∩B-B∩C-A∩C+A∩B∩C。而一个数同时可以被两个数整除,那就等价于能被俩数的最大公约数整除,能被三个数整除等价于能被三个数的最小公倍数整除,因此我们先用辗转相除法计算出abc两两的以及三个数的最小公倍数。进而可以通过这些求出某数范围内能至少被abc一个数整除的数的个数。
说用二分法,是因为题目说答案的最大可能也就是2e9,那么我们再1到2e9之间通过二分逐渐缩小范围算出最小的数,在不大于这个数的范围内有n个丑数
本思路java代码示例:
/*
*@v7fgg
*执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户
*内存消耗 :36.1 MB, 在所有 Java 提交中击败了20.00%的用户
*2020年6月14日 20:03
*/
class Solution {
public int nthUglyNumber(int n, int a, int b, int c){
long ab=zuixiaogongbei((long)a,(long)b);
long bc=zuixiaogongbei((long)b,(long)c);
long ac=zuixiaogongbei((long)a,(long)c);
long abc=zuixiaogongbei(ab,(long)c);
//二分法
long zuo=1;
long you=2000000000;
while(zuo<you){
long mid=zuo+(you-zuo)/2;
long p=mid/a+mid/b+mid/c-mid/ab-mid/ac-mid/bc+mid/abc;
if(p>n){
you=mid-1;
}
else if(p<n){
zuo=mid+1;
}
else{you=mid;}
}return (int)zuo;
}
public long zuixiaogongbei(long m,long n){
//求m和n的最小公倍数
long max=Math.max(m,n);
long min=m+n-max;
if(m>1&&n>1&&m!=n){
while(max%min!=0){
long yu=max%min;
max=min;
min=yu;
}
//上面最终的除数是最大公约数,最小公倍数等于乘积除以最大公约数
return m*n/min;
}return (long)Math.max(m,n);
}
}