【编程题】丑数Ⅲ:涉及二分查找、最大公约数、最小公倍数算法

1、题目:
请你帮忙设计一个程序,用来找出第 n 个丑数。丑数是可以被 a 或 b 或 c 整除的 正整数。

示例 :
输入:n = 3, a = 2, b = 3, c = 5
输出:4
解释:丑数序列为 2, 3, 4, 5, 6, 8, 9, 10... 其中第 3 个是 4。
提示:1 <= n, a, b, c <= 10^9

2、思路:
首先,为什么第一时间能想到二分法?
让我们观察题目,可以看到,最终状态(即n)的范围非常大。试图自底向上递推或是按照通常的自顶向下回溯显然会超时(比如动态规划、DFS等方法),面对这么大的状态空间,二分法的时间复杂度是logN,因此能够大大压缩需要遍历的状态数目。
sum(情况) = X/a + X/b + X/c - X/MCM_a_b - X/MCM_a_c - X/MCM_b_c + X/MCM_a_b_c,用MCM+下标表示最小公倍数。
具体思路不抄袭,贴出原文链接:Alfeim解题思路

3、C++代码:
注意最大公约数、最小公倍数算法

class Solution {
public:
    using LL = long long; // 使用using定义类型别名
    int nthUglyNumber(int n, int a, int b, int c) {
        LL start = min(min(a,b), c);
        LL end = start*n;

        LL MCM_a_b = LCM(a,b); //求最小公倍数
        LL MCM_a_c = LCM(a,c);
        LL MCM_b_c = LCM(b,c);
        LL MCM_a_b_c = LCM(MCM_a_b,c);

        LL res = Binary_Search( start, end, a, b, c, n, MCM_a_b, MCM_a_c, MCM_b_c, MCM_a_b_c );

        return res - min(min(res%a, res%b),res%c);
    }

// 二分查找
    LL Binary_Search(LL start, LL end, int a, int b, int c, int n, LL MCM_a_b, LL MCM_a_c, LL MCM_b_c, LL MCM_a_b_c) {
        if(start >= end) return start;

        LL mid = (end+start) >> 1; //移位代替除以2
        LL res = mid/a + mid/b + mid/c - mid/MCM_a_b - mid/MCM_a_c - mid/MCM_b_c + mid/MCM_a_b_c;
        if(res == n) 
            return mid;
        else if (res<n) {
            return Binary_Search( mid+1, end, a, b, c, n, MCM_a_b, MCM_a_c, MCM_b_c, MCM_a_b_c );
        }
        else
            return Binary_Search( start, mid-1, a, b, c, n, MCM_a_b, MCM_a_c, MCM_b_c, MCM_a_b_c );
    }

    LL LCM(LL a, LL b) { // 求最小公倍数
        LL multi = a * b;
        while(b > 0){ //最小公倍数 = a*b/(a和b的最大公约数),最大公约数可以通过辗转相除法得到。
            LL tmp = a % b;
            a = b;
            b = tmp;
        } //循环结束后a为最大公约数
        LL res = multi / a;
        return res;
    }
};

参考资料:
题目来源:力扣(LeetCode)
题目链接:https://leetcode-cn.com/problems/ugly-number-iii
解题作者:Alfeim
解题链接:https://leetcode-cn.com/problems/ugly-number-iii/solution/er-fen-fa-si-lu-pou-xi-by-alfeim/

总结:

1、最大公约数通过辗转相除法得到。
2、最小公倍数 = a*b/(a和b的最大公约数)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值