【算法通关】1201. 丑数 III——最大公约数+二分

5,287 阅读2分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

做完前面几道丑数题过来的,一下子就被坑了 这尼玛是重新定义了 丑数?

——leetcode此题热评

前言

大家好,我是一条,欢迎来到我的算法频道。

只做有趣的算法题,只为面试写算法

Question

1201. 丑数 III

难度:中等

给你四个整数:n 、a 、b 、c ,请你设计一个算法来找出第 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, 10, 12... 其中第 4 个是 6。

示例 3:

输入:n = 5, a = 2, b = 11, c = 13 输出:10 解释:丑数序列为 2, 4, 6, 8, 10, 11, 12, 13... 其中第 5 个是 10。

Solution

补充一个定理,容斥原理

容斥原理可以描述如下:

要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。

即一个区间的可以被a/b/c除的个数为 num/a+num/b+num/c (一位的) -num/(ab)-num(bc)-num(ac) (两位的)+num(abc) (三位的)

Code

/**
 * @author 一条coding
 */
class Solution {
	public int nthUglyNumber(int n, int a, int b, int c) {
		long ans = 0;
		long l = 0, r = (long) Math.min(a, Math.min(b, c)) * n;
		long ab = this.lcm(a, b);
		long ac = this.lcm(a, c);
		long bc = this.lcm(b, c);
		long abc = this.lcm(b, ac);
		while (l <= r) {
			long m = l + ((r - l) >> 1);
			long N = m / a + m / b + m / c - m / ab - m / ac - m / bc + m / abc;
			if (N < n) {
				l = m + 1;
				ans = l;
			} else {
				r = m - 1;
			}
		}
		return (int) ans;
	}

	private long gcd(long a, long b) {
		return b == 0 ? a : gcd(b, a % b);
	}

	private long lcm(long a, long b) {
		return a * b / gcd(a, b);
	}
}

最后

点赞,点赞,还TMD是点赞!