【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 or b or c.

 

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]

 

posted @ 2019-09-25 10:36  seyjs  阅读(553)  评论(0编辑  收藏  举报