已知子集大小与连续数组的子集和问题高效求解方案
高效求解固定大小的连续整数子集和问题
针对输入为1到2000的连续整数、要求返回所有大小为k且和为目标值S的子集的问题,我们可以利用数学剪枝+递归回溯的方法大幅提升效率,避免常规子集和算法的冗余计算。
核心思路
先判断解的存在性:
- 大小为
k的子集的最小和:min_sum = k*(k+1)//2(取前k个最小数) - 大小为
k的子集的最大和:max_sum = k*(2*2000 - k +1)//2(取后k个最大数) - 若目标和
S不在[min_sum, max_sum]范围内,直接返回空列表。
- 大小为
递归回溯+剪枝优化:
- 递归时从当前位置开始选择下一个数(避免生成重复子集,比如
[1,2]和[2,1]视为同一个子集) - 每一步选择数
i时,提前计算:- 剩余
k-1个数的最小可能和(从i+1开始取连续k-1个数),若该值大于剩余需要的和,直接跳过后续更大的数 - 剩余
k-1个数的最大可能和(从2000往前取k-1个数),若该值小于剩余需要的和,直接跳过当前数
- 剩余
- 递归时从当前位置开始选择下一个数(避免生成重复子集,比如
Python 示例代码
def find_k_subsets(k, target_sum, max_num=2000): result = [] # 计算最小和与最大和,判断是否存在解 min_sum = k * (k + 1) // 2 max_sum = k * (2 * max_num - k + 1) // 2 if target_sum < min_sum or target_sum > max_sum: return result def backtrack(start, remaining_k, remaining_sum, path): if remaining_k == 0: if remaining_sum == 0: result.append(path.copy()) return # 遍历从start到max_num的数 for i in range(start, max_num + 1): # 如果当前数已经大于剩余需要的和,后面的数更大,直接break if i > remaining_sum: break # 计算剩余k-1个数的最小可能和:(i+1)+(i+2)+...+(i+remaining_k-1) min_remaining = (remaining_k - 1) * (2 * i + remaining_k) // 2 if min_remaining > remaining_sum - i: break # 即使选后面最小的数也不够,不用继续遍历更大的i # 计算剩余k-1个数的最大可能和:max_num + (max_num-1) + ... + (max_num - remaining_k +2) max_remaining = (remaining_k - 1) * (2 * max_num - remaining_k + 2) // 2 if max_remaining < remaining_sum - i: continue # 即使选后面最大的数也不够,跳过当前i # 递归 path.append(i) backtrack(i + 1, remaining_k - 1, remaining_sum - i, path) path.pop() backtrack(1, k, target_sum, []) return result # 示例调用:寻找大小为3,和为6的子集 if __name__ == "__main__": subsets = find_k_subsets(3, 6) print(subsets) # 输出: [[1,2,3]]
效率说明
- 由于加入了严格的剪枝条件,该算法不会遍历所有可能的组合,尤其是当
k较小或目标和接近边界时,速度会非常快。 - 对于
k较大的情况(比如k=1000),解的数量本身会很少(甚至唯一),算法也能快速定位。
内容的提问来源于stack exchange,提问作者Fanfer123
相关产品推荐
相关产品推荐

