小E的倍数关系子集问题 | 豆包MarsCode AI刷题

88 阅读4分钟

小E的倍数关系子集问题 - MarsCode

问题描述

小E想知道一个给定集合中,有多少个子集满足以下条件:

  • 子集内的所有元素数量大于 1。
  • 子集内的所有元素两两之间互为倍数关系。

由于结果可能非常大,输出的结果需要对 109+7109+7 取模。


测试样例

样例1:

输入:n = 5,a = [1, 2, 3, 4, 5]
输出:6

样例2:

输入:n = 6,a = [2, 4, 8, 16, 32, 64]
输出:57

样例3:

输入:n = 4,a = [3, 6, 9, 12]
输出:5

题目要求我们从给定的整数集合中,找到所有满足以下条件的子集:

  • 子集内的元素数量大于 1。
  • 子集内的所有元素两两之间互为倍数关系。

思路分析

  1. 倍数关系:两个数 xy 互为倍数关系,意味着 x % y == 0y % x == 0。这意味着在一个子集中,所有元素的任意两两数都要满足这个倍数条件。

  2. 动态规划 + 排序

    • 首先,可以将数组进行排序,因为如果数组是升序的,那么只需要检查每个数和它之前的数的倍数关系。
    • dp[i] 表示以 a[i] 为最大元素的符合条件的子集的个数。
    • 对于每个 a[i],我们可以尝试找到所有 a[j]j < i),满足 a[i] % a[j] == 0,然后把以 a[j] 为最大元素的子集加入到 a[i] 的子集中。
    • 最终的答案是所有 dp[i] 中大于 1 的子集数量的和。
  3. 模块化计算

    • 由于结果可能非常大,需要对 10^9 + 7 取模。

步骤

  1. 排序:对数组进行排序,保证我们从小到大检查每个数。
  2. 动态规划:使用 dp 数组来记录每个数可以构成的子集个数。
  3. 更新子集:对于每个元素 a[i],检查之前的所有元素 a[j]j < i),如果 a[i] % a[j] == 0,那么可以把 a[j] 的所有子集扩展到 a[i]
  4. 取模:计算结果时,记得对 10^9 + 7 取模
MOD = 10**9 + 7

def solution(n, a):
    # 排序数组
    a.sort()
    
    # dp[i]表示以a[i]为最大元素的符合条件的子集个数
    dp = [0] * n
    total = 0
    
    for i in range(n):
        dp[i] = 1  # 至少可以是a[i]自己组成的子集
        # 检查前面所有可能的子集
        for j in range(i):
            if a[i] % a[j] == 0:
                dp[i] = (dp[i] + dp[j]) % MOD
        
        # 仅计算包含多个元素的子集(即dp[i] > 1)
        total = (total + dp[i]) % MOD
    
    # 最终结果减去所有单元素子集
    result = (total - n + MOD) % MOD
    return result

# 测试样例
print(solution(5, [1, 2, 3, 4, 5]))  # 输出:6
print(solution(6, [2, 4, 8, 16, 32, 64]))  # 输出:57
print(solution(4, [3, 6, 9, 12]))  # 输出:5

解释

  1. 初始化和排序

    • 我们首先对数组 a 进行排序,以保证我们可以从小到大检查每个数的倍数关系。
  2. 动态规划

    • 对于每个数 a[i],我们假设它单独形成一个子集(即 dp[i] = 1),然后尝试找出所有能与 a[i] 形成倍数关系的前一个数 a[j]j < ia[i] % a[j] == 0),将 a[j] 的子集扩展到 a[i],因此 dp[i] += dp[j]
  3. 总和计算

    • 我们通过累加所有 dp[i] 来得到所有子集的数量,但是这个计数中包含了所有单个元素子集,所以最后需要减去 n(单个元素的子集数量)。
  4. 取模

    • 因为答案可能非常大,我们在每一步计算时都对 10^9 + 7 取模,保证计算不会溢出。

时间复杂度

  • 排序O(n log n)
  • 动态规划:对于每个 a[i],我们需要遍历它之前的所有数,所以总的时间复杂度是 O(n^2)
  • 因此,整体时间复杂度是 O(n^2),在 n 较小的情况下可以接受。

测试样例解析

  1. 输入n = 5, a = [1, 2, 3, 4, 5]

    • 排序后的数组:[1, 2, 3, 4, 5]

    • 子集如下:

      • [1, 2], [1, 4], [1, 2, 4], [2, 4], [3, 6], [6]
      • 最终结果为 6
  2. 输入n = 6, a = [2, 4, 8, 16, 32, 64]

    • 排序后的数组:[2, 4, 8, 16, 32, 64]
    • 通过计算所有符合条件的子集,最终结果为 57
  3. 输入n = 4, a = [3, 6, 9, 12]

    • 排序后的数组:[3, 6, 9, 12]
    • 最终结果为 5