題目連接:5198. 醜數 IIIhtml
請你幫忙設計一個程序,用來找出第 n
個醜數。java
醜數是能夠被 a
或 b
或 c
整除的 正整數。web
輸入:n = 3, a = 2, b = 3, c = 5
輸出:4
解釋:醜數序列爲 2, 3, 4, 5, 6, 8, 9, 10… 其中第 3 個是 4。app
輸入:n = 4, a = 2, b = 3, c = 4
輸出:6
解釋:醜數序列爲 2, 3, 4, 6, 8, 9, 12… 其中第 4 個是 6。svg
輸入:n = 5, a = 2, b = 11, c = 13
輸出:10
解釋:醜數序列爲 2, 4, 6, 8, 10, 11, 12, 13… 其中第 5 個是 10。測試
輸入:n = 1000000000, a = 2, b = 217983653, c = 336916467
輸出:1999999984spa
1 <= n, a, b, c <= 10^9
1 <= a * b * c <= 10^18
[1, 2 * 10^9]
的範圍內這是我第一次碰見 醜數 這個概念。設計
若是以前見過醜數的同窗可能會發現,此題的醜數定義和【醜數 - 百度百科】的定義是不一樣的。code
此題的醜數定義:醜數是能夠被 a
或 b
或 c
整除的正整數。xml
題目比較坑的一點是示例 2 中的醜數序列是錯的,它缺乏 10。由於 10 能被 2 整除,因此 10 是醜數。
原本就沒見過醜數,它還弄個錯誤的示例,致使寫題的時候個人思路偏離。
知道醜數的定義,那麼能夠採用暴力搜索的方法找到第 n
個醜數。可是測試示例 4 的時候會 TLE。
所以需另尋他法。
給定一個數 n
,和三個數 2
,3
,5
,那麼區間 [1, n]
有多少個醜數呢?
根據定義:咱們知道 2
的倍數確定是醜數,有多少個 2
的倍數呢?固然是 n / 2
個啦(所有向下取整)。
同理 3
的倍數也是,有 n / 3
個。
5
的倍數有 n / 5
個。
如今你會發現另外一個問題,好比 6
是 2
的倍數,也是 3
的倍數。那豈不是計算了兩遍?沒錯,確實算了兩遍。所以咱們須要知道 【容斥原理 - 百度百科】。提及來陌生,可是我相信你們都用過。
所以,咱們能夠知道區間 [1, n]
的醜數個數了。即
代碼表示爲:num = n / a + n / b + n / c - n / bc - n / ac - n / ab + n / abc
須要注意的是,題目沒有說給的三個數是互質的,所以須要計算最小公倍數和最大公約數。
而後再用【二分查找 - 百度百科】逼近第 n
個醜數
。
時間複雜度: 二分查找時間複雜度爲
空間複雜度:
class Solution { public int nthUglyNumber( int n, int a, int b, int c) { long ab = lcm(a, b);// a,b的最小公倍數 long ac = lcm(a, c); long bc = lcm(b, c); long abc = lcm(ab, c); long left = 1, right = 2000000000; while (left < right) { long mid = left + right >> 1;// 中間值,+優先級高於>> // 利用容斥原理計算區間[1, mid]的醜數 long num = mid / a + mid / b + mid / c; num -= mid / bc + mid / ac + mid / ab; num += mid / abc; if (num < n) { left = mid + 1;// 取區間[mid + 1, right] } else { right = mid;// 取區間[left, mid] } } return (int) left; } // 計算最小公倍數 private long lcm( long a, long b) { return a / gcd(a, b) * b;// 須要調用gcd } // 計算最大公約數 private long gcd( long a, long b) { return b > 0 ? gcd(b, a % b) : a; } }