【leetcode】1201. Ugly Number III
题目如下:
Write a program to find the
n
-th ugly number.Ugly numbers are positive integers which are divisible by
a
orb
orc
.
Example 1:
Input: n = 3, a = 2, b = 3, c = 5 Output: 4 Explanation: The ugly numbers are 2, 3, 4, 5, 6, 8, 9, 10... The 3rd is 4.Example 2:
Input: n = 4, a = 2, b = 3, c = 4 Output: 6 Explanation: The ugly numbers are 2, 3, 4, 6, 8, 9, 10, 12... The 4th is 6.Example 3:
Input: n = 5, a = 2, b = 11, c = 13 Output: 10 Explanation: The ugly numbers are 2, 4, 6, 8, 10, 11, 12, 13... The 5th is 10.Example 4:
Input: n = 1000000000, a = 2, b = 217983653, c = 336916467 Output: 1999999984
Constraints:
1 <= n, a, b, c <= 10^9
1 <= a * b * c <= 10^18
- It's guaranteed that the result will be in range
[1, 2 * 10^9]
解题思路:先看这么一个问题,怎么求出任意一个数x,在[1,x]区间内有几个丑数?只要(x/a)+(x/b)+(x/c)即可,但是可能会有重复的值,比如a=2,b=3时,丑数6就会多计算一次,所以还需要减去( x/lcm(a,b) + x/lcm(c,b) + x/lcm(a,c) )。这里lcm(a,b)表示a和b的最小公倍数。这样是不是就好了呢?还有lcm(a,b,c)的情况,因为前面求两两最小公倍数的时候多减了一次,所以这里要再加上 x/lcm(a,b,c) ,这里就可以得到x前面有几个丑数,即为count。由于x不一定是丑数,所有只要求出小于x的最大丑数,这个丑数所在的位置就是count。由于丑数的数量随着x的增加而增加,所以用二分查找的方法很容易就可以求得指定位置的丑数。
代码如下:
class Solution(object): def nthUglyNumber(self, n, a, b, c): """ :type n: int :type a: int :type b: int :type c: int :rtype: int """ def gcd(a, b): return a if b == 0 else gcd(b, a % b) def lcm(a, b): return a * b // gcd(a, b) def getCount(v,divisor): return v / divisor divisor_list = sorted([a,b,c]) if divisor_list[2] % divisor_list[0] == 0: del divisor_list[2] if divisor_list[1] % divisor_list[0] == 0: del divisor_list[1] if len(divisor_list) == 3 and divisor_list[2] % divisor_list[1] == 0: del divisor_list[2] lcm_list = set() for i in range(len(divisor_list)): for j in range(len(divisor_list)): if i != j:lcm_list.add(lcm(divisor_list[i],divisor_list[j])) common_lcm = None if len(divisor_list) == 3: common_lcm = lcm(divisor_list[0],divisor_list[1]) common_lcm = lcm(common_lcm, divisor_list[2]) low ,high = 1, 2*(10**9) val = 0 while low <= high: #print low,high mid = (low + high)/2 #mid = 120 #if mid == 128: # pass ugly_count = 0 for i in divisor_list: ugly_count += getCount(mid,i) for i in lcm_list: ugly_count -= getCount(mid,i) if common_lcm != None: ugly_count += getCount(mid,common_lcm) if n == ugly_count: val = mid break elif n > ugly_count: low = mid + 1 else: high = mid - 1 res = [] for i in divisor_list: res.append(val/i*i) for i in lcm_list: res.append(val / i * i) return sorted(res)[-1]