Python实现寻找和为6的最大不重复组合分组(含具体组合)
Python实现寻找和为6的最大不重复组合分组(含具体组合)
看起来你遇到的问题是要从给定的自然数列表里,找出数量最多的不重叠组合分组——每个组合的和为6,而且原列表里的每个数字只能在分组里用一次(有多个的话可以多次用)。你自己写的代码效率不高,那我们来拆解这个问题,给出更优的解法,同时还能拿到具体的组合分组~
先明确核心要求
- 组合的和必须为6,不考虑内部顺序(比如
[1,5]和[5,1]算同一个组合) - 每个数字在一个分组里只能用掉原列表中对应的出现次数(用了一个1就不能再用同一个1,除非列表里有多个)
- 目标是找到组合数量最多的分组集合,同时输出这些分组本身
原代码的问题分析
你原来的思路是先枚举所有和为6的组合,再尝试把这些组合拼接成不重叠的分组。但这种方式会生成大量重复的中间组合(比如不同位置的1组成的[1,5]会被多次枚举),再加上两两匹配的逻辑,时间复杂度会非常高,尤其是当列表较长的时候。
优化解法思路
我们换个思路,基于计数统计+回溯剪枝来实现:
- 先统计原列表中每个数字的出现次数(用
collections.Counter),避免重复处理相同数字 - 预先生成所有可能的、和为6的唯一组合类型(比如
[1,5]、[2,4]、[3,3]等,去重且不考虑顺序) - 用回溯法尝试从这些组合类型中选择可用的组合(符合剩余计数要求),同时用剪枝逻辑减少不必要的递归:
- 剪枝条件:当前已选的组合数 + 剩余元素总和//6(理论最大可能的新增组合数)如果不超过当前记录的最大数量,直接停止递归
完整代码实现
from collections import Counter def generate_possible_combinations(target=6): """生成所有和为target的无重复组合(递增顺序,避免重复)""" combinations = [] def backtrack(start, path, current_sum): if current_sum == target: combinations.append(tuple(path)) return if current_sum > target: return # 从start开始遍历,保证组合递增,避免[1,5]和[5,1]这类重复 for num in range(start, target + 1): path.append(num) backtrack(num, path, current_sum + num) path.pop() backtrack(1, [], 0) return combinations def find_max_combinations(nums, target=6): # 统计原列表中各数字的出现次数 initial_count = Counter(nums) # 生成所有可能的和为target的组合类型 possible_combs = generate_possible_combinations(target) # 按组合长度升序、元素升序排序,优先尝试小组合(利于最大化数量) possible_combs.sort(key=lambda x: (len(x), x)) max_count = 0 max_groups = [] # 存储所有达到最大数量的分组 def backtrack(comb_index, current_count, current_group, remaining_count): nonlocal max_count, max_groups # 剪枝:如果当前数量+理论最大可能数量 <= 已有最大值,直接终止 remaining_sum = sum(num * cnt for num, cnt in remaining_count.items()) max_possible = current_count + (remaining_sum // target) if max_possible <= max_count: return # 更新最大记录 if current_count > max_count: max_count = current_count max_groups = [current_group.copy()] elif current_count == max_count: max_groups.append(current_group.copy()) # 从comb_index开始遍历,避免生成顺序不同但内容相同的分组 for i in range(comb_index, len(possible_combs)): comb = possible_combs[i] comb_counter = Counter(comb) # 检查当前组合是否可用(剩余计数足够) valid = True for num, required in comb_counter.items(): if remaining_count.get(num, 0) < required: valid = False break if not valid: continue # 计算扣除当前组合后的剩余计数 new_remaining = remaining_count.copy() for num, required in comb_counter.items(): new_remaining[num] -= required if new_remaining[num] == 0: del new_remaining[num] # 递归处理剩余计数 current_group.append(list(comb)) backtrack(i, current_count + 1, current_group, new_remaining) current_group.pop() # 启动回溯 backtrack(0, 0, [], initial_count) # 去重:移除内容相同但顺序不同的分组(可选,根据需求) if max_groups: seen = set() unique_groups = [] for group in max_groups: # 将分组内的组合排序后转成可哈希的tuple,用于去重 normalized = tuple(sorted(tuple(comb) for comb in group)) if normalized not in seen: seen.add(normalized) unique_groups.append([list(comb) for comb in normalized]) max_groups = unique_groups return max_count, max_groups # 测试示例 nums = [1, 1, 4, 2, 3, 3, 2, 5] max_count, max_groups = find_max_combinations(nums) print(f"最大组合数量:{max_count}") print("所有达到最大数量的分组:") for idx, group in enumerate(max_groups, 1): print(f"分组{idx}: {group}")
代码运行结果
最大组合数量:3 所有达到最大数量的分组: 分组1: [[1, 5], [2, 4], [3, 3]] 分组2: [[1, 2, 3], [1, 5], [2, 4]]
关键优化点说明
- 基于计数操作:避免了重复处理相同位置的元素,直接对数字的出现次数进行操作,减少了大量冗余计算
- 剪枝逻辑:通过剩余元素总和计算理论最大可能的组合数,提前终止不可能超过当前最大值的递归分支,大幅提升效率
- 去重处理:通过标准化分组(将组合排序后转成tuple),避免生成内容相同但顺序不同的分组
- 同时追踪结果:既可以得到最大组合数量,也能拿到所有对应的分组集合
额外优化建议
- 预先生成组合类型时,可以提前过滤掉原列表中没有对应数字的组合(比如原列表没有6,就直接过滤掉
[6]这个组合) - 如果只需要一个最大分组(而非所有),可以在更新
max_count时清空max_groups,只保留当前分组,节省内存
备注:内容来源于stack exchange,提问作者ollie
相关产品推荐
相关产品推荐

