请你帮忙设计一个程序,用来找出第 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] 的范围内
链接:https://leetcode-cn.com/problems/ugly-number-iii
看数据,n在10^9,O(n)的时间复杂度和空间复杂度根本做不来,因此动态规划什么的不用考虑了。一般这种情况下时间复杂度只能考虑O(1),O(lgn). 于是可以想到用二分枚举。
关键是,二分枚举的mid要和n进行比较,来决定分在哪个区间。现在的问题就是mid和n如何比较?
对于任意一个丑数x,我们能否确定它是第几个丑数吗?
答案是可以的。丑数的定义是能被a或b或c整除的数。那么我们只需要知道x前面能被多少个a,b,c整除(设为cnt),即[x/a]+[x/b]+[x/c].
不过这里有一个问题,假设a=2, b=3,对于6这个数来说,我们多加了一次。因为6是2和3的公倍数。这里就是容斥定理了。不知道的可以百度一下,这里主要讲讲二分枚举。
利用容斥定理可以求出cnt的值。但还是有一个问题。比如第一个样例,6的cnt是5(2+2+1),7的cnt也是5(2+2+1),并不是cnt == n时x就是丑数。也就是说,cnt == n时满足的数有一个区间。
比如满足n=5的数区间为[6,7]. 我们需要取这个区间的左边界。因此这个二分是lower_bound.
下面上代码:
#define ll long long
class Solution {
public:
ll gcd(ll a, ll b)
{
return b?gcd(b,a%b):a;
}
int nthUglyNumber(int n, int a, int b, int c) {
ll l = 0, r = INT_MAX;
ll ab = a/gcd(a,b)*b;
ll ac = a/gcd(a,c)*c;
ll cb = b/gcd(c,b)*c;
ll abc = ab/gcd(ab, c)*c;
while(l < r)
{
ll mid = l/2+r/2;
ll cnt = (mid/a+mid/b+mid/c)-(mid/ab+mid/ac+mid/cb)+mid/abc;
if(cnt < n) l = mid+1;
else r = mid;
}
return l;
}
};