第 N 个神奇数字 - LeetCode 878 - 两种思路 - 附代码

一、问题描述

        定义:一个正整数如果能被 a 或 b 整除,那么它是神奇的。

        输入:给定三个整数 n , a , b ,返回第 n 个神奇的数字。因为答案可能很大,所以返回第 n 个神奇的数字对 10^9 + 7 取模后的值。

        示例1:

        输入:n = 1,a = 2, b = 3; 输出:2; 因为 2 是 第1个被 2 或 3 整除的数;

        示例2:

        输入:n = 4,a = 2, b = 3; 输出:6; 因为 6 是 第4个被 2 或 3 整除的数:2,3,4,6;

        原题:LeetCode - 878 第 N 个神奇数字

二、解题思路

1、思路1:二分查找

        假定区间从0开始,往正无穷增长,容易看出,区间[0,x] 包含的“神奇数字”个数是单调递增的,也就是说,x越大,包含的神奇数字越多,假设 a 和 b 的最小公倍数为 c,根据容斥原理,神奇数字关于 x 的函数为 num = f(x) = floor(x/a) + floor(x/b) - floor(x/c);同时,对于给定的n,我们知道 x 一定不会超过 n * min(a,b)。根据上面两点,我们可以在区间[0, n * min(a,b)] 进行二分查找,直至找到num = n。注意查找时要将端点移动到分段的左端点才是n。

2、思路2:数学周期

        假设 a 和 b 的最小公倍数为 c,不难发现,任意 [ k * c, (k + 1) * c] 区间内,含有的神奇数字个数是相同的,k为从0开始的任意自然数。根据这个思路,确定一个神奇数字,只需要确定他在哪个周期、周期内偏移为多少即可。

三、C++代码

1、思路1:二分查找

#include <iostream>

using namespace std;

class Solution {
public:
    int MOD = 1000000007;
    long long gcd(long long a, long long b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
    long long get_num(long long x, long long a, long long b, long long c) {
        return (long long)(x / a + x / b) - x / c;
    }
    int nthMagicalNumber(int n, int a, int b) {
        long long c = ((long long)(a)*b) / (a > b ? gcd(a, b) : gcd(b, a));
        long long l = (a > b ? b : a);
        long long r = n * l;
        long long m;
        while (r >= l) {
            m = (l + r) / 2;
            if (get_num(m, a, b, c) >= n)
                r = m - 1;
            else
                l = m + 1;
        }
        return (r + 1) % MOD;
    }
};

int main()
{
    Solution s;
    cout << "Test 1 : a = 2, b = 3 : " << endl;
    cout << "S(1,2,3)  Excepted:2  Actually:" << s.nthMagicalNumber(1, 2, 3) << endl;
    cout << "S(2,2,3)  Excepted:3  Actually:" << s.nthMagicalNumber(2, 2, 3) << endl;
    cout << "S(3,2,3)  Excepted:4  Actually:" << s.nthMagicalNumber(3, 2, 3) << endl;
    cout << "S(4,2,3)  Excepted:6  Actually:" << s.nthMagicalNumber(4, 2, 3) << endl;
    cout << "Test 2 : a = 3, b = 2 : " << endl;
    cout << "S(4,3,2)  Excepted:6  Actually:" << s.nthMagicalNumber(4, 3, 2) << endl;
    cout << "Test 3 : a = 3, b = 3 : " << endl;
    cout << "S(3,3,3)  Excepted:9  Actually:" << s.nthMagicalNumber(3, 3, 3) << endl;
    return 0;
}

2、思路2:数学周期

#include <iostream>
#include <vector>
//#define MOD 1000000007

using namespace std;

class Solution1 {
public:
    int MOD = 1000000007;
    int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
    int nthMagicalNumber(int n, int a, int b) {
        int space_len = (a > b ? a * b / gcd(a, b) : a * b / gcd(b, a));
        int a_capatical = space_len / a;
        int b_capatical = space_len / b;
        int space_capatical = a_capatical + b_capatical - 1;
        vector<int> space;
        {
            int ptr_a = 1, ptr_b = 1;
            space.push_back(0);
            while (ptr_a < a_capatical || ptr_b < b_capatical) {
                if (ptr_a * a < ptr_b * b) {
                    space.push_back(ptr_a * a);
                    ptr_a++;
                }
                else {
                    space.push_back(ptr_b * b);
                    ptr_b++;
                }
            }
            space.push_back(space_len);
        }
        int space_num = n / space_capatical;
        int additional_step = n % space_capatical;
        return (int)((((long long)(space_len % MOD) * (space_num % MOD) % MOD) + space[additional_step] % MOD) % MOD);
    }
};

int main()
{
    Solution1 s1;
    cout << "Test 1 : a = 2, b = 3 : " << endl;
    cout << "S(1,2,3)  Excepted:2  Actually:" << s1.nthMagicalNumber(1, 2, 3) << endl;
    cout << "S(2,2,3)  Excepted:3  Actually:" << s1.nthMagicalNumber(2, 2, 3) << endl;
    cout << "S(3,2,3)  Excepted:4  Actually:" << s1.nthMagicalNumber(3, 2, 3) << endl;
    cout << "S(4,2,3)  Excepted:6  Actually:" << s1.nthMagicalNumber(4, 2, 3) << endl;
    cout << "Test 2 : a = 3, b = 2 : " << endl;
    cout << "S(4,3,2)  Excepted:6  Actually:" << s1.nthMagicalNumber(4, 3, 2) << endl;
    cout << "Test 3 : a = 3, b = 3 : " << endl;
    cout << "S(3,3,3)  Excepted:9  Actually:" << s1.nthMagicalNumber(3, 3, 3) << endl;
    return 0;
}

四、运行结果

1、思路1:二分查找

 

2、思路2:数学周期

五、复杂度

        时间复杂度:O(log len) = O(log (n * min(a,b)));

        空间复杂度:O(1);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值