这个问题可以通过暴力搜索的方式来解决,但考虑到 n 的最大值为 20,因此可以利用递归和分治的策略来优化计算。具体的思路是使用递归来枚举每个目标的选择,并使用分治法来减少递归的深度,避免生成过多的组合。
思路
-
选择方式的组合:
- 对于每个目标指标
f[i],我们有两个变换值f[i][0]和f[i][1],可以从中选择一个进行变换。 - 因此,问题的本质是计算所有可能的选择组合,得到一个乘积,然后判断这个乘积是否在区间
[L, R]内。
- 对于每个目标指标
-
暴力枚举:
- 暴力方法是对每个目标,选择其两个变换值之一进行计算,最后计算出所有组合的乘积。每次乘积的结果都与区间
[L, R]比较,符合条件的计数。
- 暴力方法是对每个目标,选择其两个变换值之一进行计算,最后计算出所有组合的乘积。每次乘积的结果都与区间
-
优化方法:
- 由于
n的上限是 20,我们可以采用分治法,将问题分成两个子问题。假设我们将n个目标分成两个部分,每部分最多包含 10 个目标。对于每个部分,生成所有可能的组合乘积,然后将这两个部分的乘积组合进行匹配。
- 由于
-
高效匹配:
- 对于每个子问题,生成所有可能的乘积组合并排序。然后,使用双指针或者二分查找来找到符合要求的组合数。
具体步骤
- 对目标数组
f进行分割,将问题分成两个子问题。 - 生成每个子问题的所有可能乘积。
- 对两个子问题的乘积进行合并,计算符合条件的组合数。
代码实现
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
代码解释
-
分割问题:
- 我们首先将目标指标数组
f分为两部分first_half和second_half,每部分包含大约一半的目标。
- 我们首先将目标指标数组
-
生成组合:
- 对于每个部分,使用
itertools.product生成所有可能的选择组合。每个组合的乘积通过prod函数计算。
- 对于每个部分,使用
-
二分查找:
- 对于第一个部分的每一个乘积,我们使用二分查找来在第二个部分的所有组合中找到符合区间
[L, R]的乘积数目。 - 使用
bisect_left和bisect_right来找到第二部分乘积的有效区间。
- 对于第一个部分的每一个乘积,我们使用二分查找来在第二个部分的所有组合中找到符合区间
-
最终结果:
- 通过累加每个乘积组合数,最终得到符合条件的选择组合数量。
复杂度分析
- 时间复杂度:由于我们将问题分为两个子问题,每个子问题最多有 2n/22n/2 种选择组合。生成所有组合并计算乘积的时间复杂度为 O(2n/2)O(2n/2)。二分查找的时间复杂度为 O(log(2n/2))O(log(2n/2)),因此整体时间复杂度为 O(2n/2log2n/2)O(2n/2log2n/2),即 O(2n/2⋅n)O(2n/2⋅n)。
- 空间复杂度:我们存储了两个部分的所有组合,每个部分最多有 2n/22n/2 个组合,因此空间复杂度为 O(2n/2)O(2n/2)。
测试样例
-
样例1:
输入:
n = 2, f = [[1, 2], [3, 4]], L = 1, R = 6输出:
3解释:组合有
1*3 = 3, 1*4 = 4, 2*3 = 6,这些组合符合条件。 -
样例2:
输入:
n = 2, f = [[1, 2], [3, 4]], L = 4, R = 6输出:
2解释:组合有
1*4 = 4, 2*3 = 6,符合条件。 -
样例3:
输入:
n = 3, f = [[1, 2], [3, 5], [2, 4]], L = 10, R = 50输出:
7解释:有7种组合满足条件。
这样就完成了该问题的求解,利用分治和二分查找,避免了暴力搜索导致的时间复杂度爆炸问题。