解决方案背后的直觉

时间:2018-08-05 00:50:03

标签: c++ algorithm binary-search

我正在尝试解决关于LeetCode.com的问题:

  

如果正整数可以被AB整除,那么它是神奇的。
  返回第N个幻数。由于答案可能非常大,请以10^9 + 7为模返回。例如,对于N = 5, A = 2, B = 4,输出为10

highly upvoted solution建议“搜索”答案。但是我不明白如何将其建模为搜索问题-也是二进制搜索问题。有人可以指出背后的直觉吗?

int nthMagicalNumber(int N, int A, int B) {
    long lcm = A * B / __gcd(A, B), l = 2, r = 1e14, mod = 1e9 + 7;
    while (l < r) {
        long m = (l + r) / 2;
        if (m / A + m / B - m / lcm < N) l = m + 1;
        else r = m;
    }
    return l % mod;
}

谢谢!

2 个答案:

答案 0 :(得分:1)

对于给定的数字m,我们可以找出比m小的幻数:加m/A(被A整除的数字的数量小于m)和m/B(小于B的可被m整除的数的计数)减去m/lcm(可被整除的数的计数) AB都小于m,以避免重复计算)。这里的lcmAB的最小公倍数;被AB整除的所有数字都是lcm的倍数。

从这里开始,使用二进制搜索很容易。从足够大的间隔开始(根据问题陈述中提供的输入界限,证明初始范围足够大,留给读者练习)。找到中间的m,找出比该中间数小的幻数。如果该数量大于N,则m会超调,我们需要进一步考虑左半部分。否则,m会下冲,我们会搜索右半部分。


此方法可以改进。给定序列X_i=i*AY_i=i*B,我们考虑序列Z,它是XY的有序并集;我们的任务是找到Z_N。显而易见,Z可以用重复模式描述:到达L=lcm(A, B)之后,相同的模式会重复自身上移L,然后再次上移{{1 }}等。

例如,如果2*LA=2,则序列B=3如下所示:

Z

很容易看到四个数字的模式,一次又一次地重复,每次移动2 3 4 6 | 8 9 10 12 | 14 15 16 18 | ...

在通常情况下,此模式的长度为6 = L = LCM(2, 3):来自序列M = L/A + L/B - 1的数字X的数量加上序列<= L的数量Y减去1,因为<= L本身被计算两次。因此,L

考虑到这一点,找到Z_{i+k*M} = Z_i + k*L就足够了。为此,将二进制搜索应用于Z_N范围就足够了-仅搜索重复模式的第一次出现,因为序列的其余部分可以轻松地从中得出。

答案 1 :(得分:0)

这个想法是,给定数字m,可以通过添加m的倍数来计算小于或等于A的幻数的数量({{ 1}}),其中m / AB)的倍数减去m / BAB)的倍数数了两次。

现在解决方案就是最小的数字m / lcm,其中小于或等于m的幻数正好是m。可以使用二进制搜索找到它。