题目
请你帮忙设计一个程序,用来找出第 n 个丑数。
丑数是可以被 a 或 b 或 c 整除的 正整数。
示例 1:
输入:n = 3, a = 2, b = 3, c = 5
输出:4
解释:丑数序列为 2, 3, 4, 5, 6, 8, 9, 10… 其中第 3 个是 4。
示例 2:
输入:n = 4, a = 2, b = 3, c = 4
输出:6
解释:丑数序列为 2, 3, 4, 6, 8, 9, 12… 其中第 4 个是 6。
示例 3:
输入:n = 5, a = 2, b = 11, c = 13
输出:10
解释:丑数序列为 2, 4, 6, 8, 10, 11, 12, 13… 其中第 5 个是 10。
示例 4:
输入:n = 1000000000, a = 2, b = 217983653, c = 336916467
输出:1999999984
提示:
1 <= n, a, b, c <= 10^9
1 <= a * b * c <= 10^18
本题结果在 [1, 2 * 10^9] 的范围内
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ugly-number-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题
验证方法
输入:
1000000000
2
217983653
336916467
正确答案:
1999999984
第一版
直接通过遍历的方式获取,该方法比较笨,但是可以通过该方法验证结果是否准确。后续完整代码会提供用于验证其他方法的结果。
int nthUglyNumber(int n, int a, int b, int c){
int index = 0;
for(int i = a; i < 2000000000; i ++){
if(!(i%a) || !(i%b) || !(i%c)){
if(++index == n){
return i;
}
continue;
}
}
return 0;
}
结果:
超出时间限制
第二版
通过二分法定位最终结果。
//求最小公倍数
long fun(int a, int b){
long m = a, n = b, c;
while(b!= 0){
c=a%b;
a=b;
b=c;
}
return (m*n/a);
}
int nthUglyNumber(int n, int a, int b, int c){
long temp = 0;
long count = 0;
long max = n*a;
long lower = a, hight = max > 2000000000 ? 2000000000 : max;
if((b%a) || (c%a)) {
//b c不能舍弃,需要计算a b c
long m_ab = fun(a,b);
long m_ac = fun(a,c);
long m_bc = fun(b,c);
long m_abc = fun((int)m_ab, c);
while(lower <= hight) {
temp = (lower+hight) >> 1;
count = (temp/a + temp/b + temp/c - temp/m_ab - temp/m_ac - temp/m_bc + temp/m_abc);
if(count == n && (!(temp%a) || !(temp%b) || !(temp%c))) {
return temp;
}else{
if(count < n){
lower = temp + 1;
} else {
hight = temp - 1;
}
}
}
return (int)hight;
} else {
//c被抛弃,只需要计算 a
return a*n;
}
}
结果:
1999999984
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int nthUglyNumber2(int n, int a, int b, int c){
int count = 0;
for(int i = a; i < 2000000000; i ++){
if(!(i%a) || !(i%b) || !(i%c)){
//printf("%d\t",i);
if(++ count == n) {
return i;
}
}
//if(!(i%30)){
// printf("\n");
//}
}
return count;
}
long fun(int a, int b) {
long t;
int m, n, c;
m=a; n=b;
while(b!=0) /* 余数不为0,继续相除,直到余数为0 */
{
c=a%b;
a=b;
b=c;
}
t = (long)m*n/a;
//printf("The largest common divisor:%d\n", a);
//printf("The least common multiple:%ld\n", t);
return t;
}
int nthUglyNumber(int n, int a, int b, int c){
long temp = 0;
int count = 0;
clock_t start, finish;
start = clock();
long m_ab = fun(a,b);
long m_bc = fun(b,c);
long m_ac = fun(a,c);
long m_abc = fun(m_ab,c);
finish = clock();
printf(" m fun cost time : %lu\n", finish-start);
long max = n*a;
int lower = a, hight = max > 2000000000 ? 2000000000 : (int)max;
while(lower < hight) {
temp = ((long)lower+hight) >> 1;
count = (temp/a + temp/b + temp/c - temp/m_ab - temp/m_ac - temp/m_bc + temp/m_abc);
//printf("%d--%d\n", lower, hight);
//printf("temp: %ld -- count:%d\n", temp , count);
if(count == n && (!(temp%a) || !(temp%b) || !(temp%c))) {
return temp;
}else{
if(count < n){
lower = temp + 1;
} else {
hight = temp - 1;
}
}
}
return lower;
}
int main(int argc, char* argv[]){
if(argc < 5){
printf("pls input 4 args!\n");
return 0;
}
int n = atoi(argv[1]);
int a = atoi(argv[2]);
int b = atoi(argv[3]);
int c = atoi(argv[4]);
int ret, ret2;
clock_t start, finish;
printf("------- normal fun start! --------\n");
start = clock();
ret2 = nthUglyNumber2(n,a,b,c);
finish = clock();
printf("------- normal fun over! --------\n");
printf(" normal fun cost time : %lu\n", finish-start);
printf("------- samrt fun start! --------\n");
start = clock();
ret = nthUglyNumber(n,a,b,c);
finish = clock();
printf("------- smart fun over! --------\n");
printf(" smart fun cost time : %lu\n", finish-start);
printf("ret: %d == %d?\n",ret, ret2);
return 0;
}
结果:
./a.out 1000000000 2 217983653 336916467
------- normal fun start! --------
------- normal fun over! --------
normal fun cost time : 12890148
------- samrt fun start! --------
------- smart fun over! --------
smart fun cost time : 3
ret: 1999999984 == 1999999984?
总结
最终还是二分法比较快速定位到正确值,完整代码可以调试代码与验证结果。