📜  GCD 等于给定数组的 GCD 的最小子序列(1)

📅  最后修改于: 2023-12-03 15:15:13.835000             🧑  作者: Mango

GCD 等于给定数组的 GCD 的最小子序列

问题描述

给定一个整数数组 nums,找到具有最大长度的子序列,使得该序列中所有元素的最大公约数(GCD)等于数组 nums 的 GCD。

解法
思路

首先,我们需要找到数组 nums 的 GCD。然后,我们可以尝试从 nums 的所有子序列中找到符合条件的子序列。但是这样的时间复杂度为 O(2^n),即使使用一些优化技巧,时间复杂度也很高。

我们可以尝试一些优化技巧,如使用记忆化搜索的方法来避免重复计算。但是最好的方法是使用动态规划。

动态规划

令 dp[i][j] 表示以 nums[i] 结尾、GCD 为 j 的最小子序列的长度。

因为一个数的约数一定小于等于它本身,所以对于 dp[i][j],我们只需要考虑 nums[0] 到 nums[i-1] 中 GCD 为 j 的最小子序列即可。

则有状态转移方程:

  • 当 nums[i] % j == 0 时:dp[i][j] = dp[i-1][j] + 1;
  • 当 nums[i] % j != 0 时:dp[i][j] = dp[i-1][gcd(j,nums[i])];

则最终的答案为 dp[n-1][gcd(nums[0],nums[1],...,nums[n-1])]。

代码实现
def findSubsequence(nums: List[int]) -> int:
    n = len(nums)
    gcd = nums[0]
    # 计算数组的 GCD
    for i in range(1, n):
        gcd = math.gcd(gcd, nums[i])
    # 特判:当 GCD 为 1 时,整个数组就是符合条件的子序列
    if gcd == 1:
        return n
    # 动态规划
    dp = [[0] * (gcd+1) for _ in range(n)]
    dp[0][nums[0]] = 1
    for i in range(1, n):
        for j in range(1, gcd+1):
            if nums[i] % j == 0:
                dp[i][j] = dp[i-1][j] + 1
            else:
                dp[i][j] = dp[i-1][math.gcd(j, nums[i])]
    return max(dp[n-1])
总结

本题需要计算数组的 GCD,然后使用动态规划来寻找符合条件的子序列。在使用动态规划时,我们需要考虑 GCD 的约数,然后使用状态转移方程来更新 dp 数组。

本题的时间复杂度为 O(n * GCD(n)),空间复杂度为 O(n * GCD(n)),其中 GCD(n) 表示 n 的约数个数。由于 GCD(n) 不会超过 n 的约数个数,所以本题的时间复杂度和空间复杂度均为 O(n * sqrt(n))。

参考资料