《最小操作数使数组变为 Firm 数组》和《小C的数字倍数问题》| 豆包MarsCode AI刷题

119 阅读4分钟

今天我们将在豆包MarsCode AI刷题平台上,完成《最小操作数使数组变为 Firm 数组》与《小C的数字倍数问题》这两个算法问题,通过这些练习提升用户解决此类问题的能力

《最小操作数使数组变为 Firm 数组》题面如下:

image.png

题目要求将一个数组 A 转换为“Firm”数组,即数组中所有元素的最大公约数(GCD)大于 1。可以通过增加或减少数组中的任意元素来实现这一目标,并且需要计算出最少的操作次数。

数据结构选择

  • 素数列表:我们选择了一个素数列表 primes,用于存储小于等于某个上限的素数。这个列表将用于后续的计算。
  • 布尔数组:在 findPrimes 函数中,使用了一个布尔数组 is_prime 来标记每个数是否为素数。

算法步骤

  1. 生成素数列表

    • 使用埃拉托斯特尼筛法(Sieve of Eratosthenes)来生成小于等于 1000 的素数列表。
  2. 计算最小操作次数

    • 对于每个素数 p,计算将数组 A 中的每个元素调整为 p 的倍数所需的最小操作次数。
    • 具体来说,对于数组中的每个元素 i,计算 i % pp - i % p 的较小值,并将这些值累加起来,得到将整个数组调整为 p 的倍数所需的操作次数。
    • 遍历所有素数,并记录最小的操作次数。

具体实现

// 生成素数列表
vector<int> primes;
void findPrimes(int n) {
    if (n <= 1) return;
    vector<bool> is_prime(n, true);
    is_prime[0] = is_prime[1] = false; // 0 and 1 are not prime numbers

    for (int i = 2; i <= sqrt(n); ++i) {
        if (is_prime[i]) {
            for (int j = i * i; j < n; j += i) {
                is_prime[j] = false;
            }
        }
    }
    
    for (int i = 2; i <= n; ++i) {
        if (is_prime[i]) {
            primes.push_back(i);
        }
    }

}

// 计算最小操作次数
int solution(int N, const std::vector<int>& A) {
    // write code here
    int ret = INT_MAX;
    findPrimes(1000);
    for(int p : primes){
        int cur=0;
        for(int i : A){
            cur += min(i % p, p - i % p);
        }
        ret = min(ret, cur);
    }

    return ret;
}

《小C的数字倍数问题》题面如下:

image.png

问题理解

我们需要计算在区间 [l, r] 内,有多少个数是 a 的倍数,或者是 b 的倍数,或者是 c 的倍数。

解题思路

  1. 基本思路

    • 计算区间 [l, r]a 的倍数的个数。
    • 计算区间 [l, r]b 的倍数的个数。
    • 计算区间 [l, r]c 的倍数的个数。
    • 由于 abc 的倍数可能有重叠,我们需要使用容斥原理来避免重复计数。
  2. 容斥原理

    • 计算 ab 的倍数的重叠部分。
    • 计算 ac 的倍数的重叠部分。
    • 计算 bc 的倍数的重叠部分。
    • 计算 abc 的倍数的重叠部分。
  3. 具体步骤

    • 计算每个数的倍数个数:count(x) = r/x - (l-1)/x
    • 使用容斥原理计算总的倍数个数。

代码实现思路

  1. 计算最小公倍数

    • 使用 lcm 函数计算两个数的最小公倍数。
  2. 处理重复倍数

    • 通过排序和去除重复的倍数因子,简化后续的容斥原理计算。
  3. 容斥原理应用

    • 根据 base 数组的大小,分别处理不同数量的因子组合。

具体实现

int lcm(int a, int b){
    return a * b / __gcd(a, b);
}

int solution(int a, int b, int c, int l, int r) {
    // write code here
    int ret=0;
    vector<int> base = {a,b,c};
    sort(base.begin(), base.end());
    if(base[2] % base[0] == 0 || base[2] % base[1] == 0){
        base.pop_back();
        if(base[1] % base[0] == 0){
            base.pop_back();
        }
    } else {
        if(base[1] % base[0] == 0){
            base[1] = base[2];
            base.pop_back();
        }
    }
    for(int i : base){
        ret += r/i - (l-1)/i;
    }
    if(base.size() == 3){
        a = base[0], b = base[1], c = base[2];
        ret -= r/lcm(a,b) - (l-1)/lcm(a,b) + r/lcm(a,c) - (l-1)/lcm(a,c) + r/lcm(b,c) - (l-1)/lcm(b,c);
    } else if(base.size() == 2){
        a = base[0], b = base[1];
        ret -= r/lcm(a,b) - (l-1)/lcm(a,b);
    }
    
    return ret;
}

借助豆包MarsCode AI刷题平台,我们不仅高效地解决了《最小操作数使数组变为 Firm 数组》和《小C的数字倍数问题》,还加深了对相关算法和数据结构的理解,后续会借助豆包MarsCode AI给大家展示更多题目的解法。