[LeetCode] 1201. Ugly Number III 丑陋數之三



An ugly number is a positive integer that is divisible by ab, or c.

Given four integers nab, and c, return the nth ugly number.

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 is guaranteed that the result will be in range [1, 2 * 10^9].

這道題是丑陋數系列的第三道題,前兩道分別是 Ugly Number IIUgly Number。這里可能有的童鞋會說這不就是把第二道中的 2,3,和5換成了這里的 a,b,和c么,就想着直接套用前一道的方法來做。然而這道題的丑陋數和之前兩道的定義卻不太相同,之前兩道定義的丑陋數是只有質數因子 2,3,5 的數字,而這里定義的是可以被 a,b,和c整除的數字,就更為寬泛了,只要能被 a,b,和c中的任意一個或多個整除的數字都是丑陋數,不管還能不能被其他別的數字整除。對於 [1, num] 范圍內的整數,能被a整除的數字的個數為 num/a,而能被a或b整除的數字卻不是 num/a + num/b,因為會存在同時被a和b整除的數字,若a和b是兩個質數,比如2和3,那么就是 num/a + num/b - num/ab,但a和b不一定互質的兩個數字,可能含有共同的非1因子,所以更為嚴謹的寫法就是 num/a + num/b - num/lcm(a,b),這里的 lcm 指的是最小公倍數 Least Common Multiple,小學數學里應該都學過。同理,對於 [1, num] 范圍內的整數,能被a,b,或c整除的數字的個數為 num/a + num/b + num/c - num/lcm(a,b) - num/lcm(b,c) - num/lcm(a,c) + num/lcm(a,b,c),同時腦海中可以出現那個經典的三圓相交的圖形,論壇上的帖子也有這個圖形。

這里的最小公倍數用代碼怎么求呢,還是通過小學數學知識,兩個數字的最小公倍數等於二者之積除以最大公約數 Greatest Common Divisor。而求 gcd 在之前的題目中就用過了,比如 Max Points on a Line,甚至還有求字符串 gcd 的題目 Greatest Common Divisor of Strings。既然能有一個公式可以快速的計算出某個范圍內丑陋數的總個數,而且題目也給了所求結果的范圍 [1, 2 * 10^9],用二分搜索法簡直是再合適不過了,這是博主之前的總結帖 LeetCode Binary Search Summary 二分搜索法小結 中的第四類。確定了 left 和 right 的初始范圍之后,就可以折中求出 mid 了,然后代入上面的公式求出 [1, mid] 范圍內共有 cnt 個丑陋數字,並用 cnt 和給定的n進行比較,若 cnt 小於n,則 left 更新為 mid+1,反之,right 更新為 mid,最終返回 right 即可,參見代碼如下:


class Solution {
public:
    int nthUglyNumber(int n, int a, int b, int c) {
        int left = 0, right = 2e9;
        while (left < right) {
            int mid = left + (right - left) / 2;
            int cnt = mid / a + mid / b + mid / c - mid / lcm(a, b) - mid / lcm(b, c) - mid / lcm(a, c) + mid / lcm(a, lcm(b, c));
            if (cnt < n) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return right;
    }
    long gcd(long a, long b) {
        return a == 0 ? b : gcd(b % a, a);
    }
    long lcm(long a, long b) {
        return a * b / gcd(a, b);
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1201


類似題目:

Ugly Number II

Ugly Number

Super Ugly Number

Max Points on a Line


參考資料:

https://leetcode.com/problems/ugly-number-iii/

https://leetcode.com/problems/ugly-number-iii/discuss/387539/cpp-Binary-Search-with-picture-and-Binary-Search-Template

https://leetcode.com/problems/ugly-number-iii/discuss/387780/JavaC%2B%2B-Binary-Search-with-Venn-Diagram-Explain-Math-Formula


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM