代码练习:融合目标计算| 豆包MarsCode AI刷题

61 阅读4分钟

这个问题可以通过暴力搜索的方式来解决,但考虑到 n 的最大值为 20,因此可以利用递归和分治的策略来优化计算。具体的思路是使用递归来枚举每个目标的选择,并使用分治法来减少递归的深度,避免生成过多的组合。

思路

  1. 选择方式的组合

    • 对于每个目标指标 f[i],我们有两个变换值 f[i][0] 和 f[i][1],可以从中选择一个进行变换。
    • 因此,问题的本质是计算所有可能的选择组合,得到一个乘积,然后判断这个乘积是否在区间 [L, R] 内。
  2. 暴力枚举

    • 暴力方法是对每个目标,选择其两个变换值之一进行计算,最后计算出所有组合的乘积。每次乘积的结果都与区间 [L, R] 比较,符合条件的计数。
  3. 优化方法

    • 由于 n 的上限是 20,我们可以采用分治法,将问题分成两个子问题。假设我们将 n 个目标分成两个部分,每部分最多包含 10 个目标。对于每个部分,生成所有可能的组合乘积,然后将这两个部分的乘积组合进行匹配。
  4. 高效匹配

    • 对于每个子问题,生成所有可能的乘积组合并排序。然后,使用双指针或者二分查找来找到符合要求的组合数。

具体步骤

  1. 对目标数组 f 进行分割,将问题分成两个子问题。
  2. 生成每个子问题的所有可能乘积。
  3. 对两个子问题的乘积进行合并,计算符合条件的组合数。

代码实现

pythonCopy Code
from itertools import product
from bisect import bisect_left, bisect_right

def solution(n: int, f: list[list[int]], L: int, R: int) -> int:
    # Step 1: Divide the problem into two halves
    half = n // 2
    first_half = f[:half]
    second_half = f[half:]
    
    # Step 2: Generate all product combinations for each half
    def generate_combinations(f):
        combinations = []
        for choice in product(*f):
            combinations.append(prod(choice))
        return combinations
    
    def prod(lst):
        result = 1
        for num in lst:
            result *= num
        return result
    
    # Generate combinations for both halves
    first_combinations = generate_combinations(first_half)
    second_combinations = generate_combinations(second_half)
    
    # Step 3: Sort the second half combinations to facilitate binary search
    second_combinations.sort()
    
    # Step 4: Count valid combinations
    count = 0
    for val in first_combinations:
        lower_bound = L // val if L % val == 0 else (L // val) + 1
        upper_bound = R // val
        # Find the valid range in the second_combinations using binary search
        left_idx = bisect_left(second_combinations, lower_bound)
        right_idx = bisect_right(second_combinations, upper_bound)
        count += (right_idx - left_idx)
    
    return count

# Test cases
if __name__ == "__main__":
    print(solution(2, [[1, 2], [3, 4]], 1, 6))  # Expected output: 3
    print(solution(2, [[1, 2], [3, 4]], 4, 6))  # Expected output: 2
    print(solution(3, [[1, 2], [3, 5], [2, 4]], 10, 50))  # Expected output: 7

代码解释

  1. 分割问题

    • 我们首先将目标指标数组 f 分为两部分 first_half 和 second_half,每部分包含大约一半的目标。
  2. 生成组合

    • 对于每个部分,使用 itertools.product 生成所有可能的选择组合。每个组合的乘积通过 prod 函数计算。
  3. 二分查找

    • 对于第一个部分的每一个乘积,我们使用二分查找来在第二个部分的所有组合中找到符合区间 [L, R] 的乘积数目。
    • 使用 bisect_left 和 bisect_right 来找到第二部分乘积的有效区间。
  4. 最终结果

    • 通过累加每个乘积组合数,最终得到符合条件的选择组合数量。

复杂度分析

  • 时间复杂度:由于我们将问题分为两个子问题,每个子问题最多有 2n/22n/2 种选择组合。生成所有组合并计算乘积的时间复杂度为 O(2n/2)O(2n/2)。二分查找的时间复杂度为 O(log⁡(2n/2))O(log(2n/2)),因此整体时间复杂度为 O(2n/2log⁡2n/2)O(2n/2log2n/2),即 O(2n/2⋅n)O(2n/2⋅n)。
  • 空间复杂度:我们存储了两个部分的所有组合,每个部分最多有 2n/22n/2 个组合,因此空间复杂度为 O(2n/2)O(2n/2)。

测试样例

  1. 样例1

    输入:n = 2, f = [[1, 2], [3, 4]], L = 1, R = 6

    输出:3

    解释:组合有 1*3 = 3, 1*4 = 4, 2*3 = 6,这些组合符合条件。

  2. 样例2

    输入:n = 2, f = [[1, 2], [3, 4]], L = 4, R = 6

    输出:2

    解释:组合有 1*4 = 4, 2*3 = 6,符合条件。

  3. 样例3

    输入:n = 3, f = [[1, 2], [3, 5], [2, 4]], L = 10, R = 50

    输出:7

    解释:有7种组合满足条件。

这样就完成了该问题的求解,利用分治和二分查找,避免了暴力搜索导致的时间复杂度爆炸问题。