从集合的集合中获取指定组合的方法及计数问题
优化解法:从多集合选元素组成指定组合的计数问题
问题明确
给定n个集合 ( S_1, S_2, ..., S_n ),以及大小为n的目标集合 ( T ),需要:
- 判断是否能从每个集合选一个元素,使得选出的元素恰好构成T(无重复、全覆盖);
- 若可行,计算所有符合条件的选择方式数量。
暴力枚举的时间复杂度是各集合大小的乘积,当n或集合规模较大时完全不可行,以下是几种高效优化方案:
方案1:回溯剪枝
通过提前排除已选元素的分支,大幅减少无效遍历:
- 维护已选元素集合和当前处理的集合索引;
- 遍历当前集合的元素时,仅保留「属于T且未被选过」的元素,递归处理下一个集合;
- 递归到最后一个集合时,若已选元素正好是T,计数加1;
- 回溯时移除当前元素,继续尝试其他可能。
代码示例(Python)
def count_valid_selections(sets, target): target_set = set(target) n = len(sets) count = 0 def backtrack(index, selected): nonlocal count if index == n: if selected == target_set: count += 1 return for num in sets[index]: if num in target_set and num not in selected: selected.add(num) backtrack(index + 1, selected) selected.remove(num) backtrack(0, set()) return count if count > 0 else None # 返回数量,None表示不可行
适用场景:n≤15,目标元素无重复,剪枝效率极高。
方案2:状态压缩动态规划
用二进制掩码表示已选元素的状态,适合n≤20的场景:
- 先将目标T中的元素映射为0~n-1的位索引;
- 定义
dp[mask]表示已选元素对应掩码为mask时的方案数; - 初始状态
dp[0] = 1(未选任何元素时1种方案); - 遍历每个集合,对每个现有状态,若集合中的元素未被选过,则更新新状态的方案数;
- 最终结果为
dp[full_mask](full_mask是所有位为1的掩码),若为0则不可行。
代码示例(Python)
def count_valid_selections_dp(sets, target): target_map = {num: idx for idx, num in enumerate(target)} n = len(sets) full_mask = (1 << n) - 1 dp = [0] * (1 << n) dp[0] = 1 for s in sets: new_dp = dp.copy() for mask in range(full_mask, -1, -1): if dp[mask] == 0: continue for num in s: if num not in target_map: continue bit = target_map[num] if not (mask & (1 << bit)): new_dp[mask | (1 << bit)] += dp[mask] dp = new_dp result = dp[full_mask] return result if result > 0 else None
时间复杂度:( O(n * 2^n * k) ),k为集合平均大小,n≤20时计算量可控。
方案3:二分图完美匹配计数
将问题转化为二分图的完美匹配计数:
- 左节点:n个集合;
- 右节点:T中的n个元素;
- 若集合i包含元素t,则左节点i与右节点t连边;
- 求该二分图的完美匹配数量,存在匹配则可行,数量即为方案数。
实现思路
可以用高斯消元计算矩阵行列式的方法(将邻接矩阵转为模质数的矩阵,行列式的值即为完美匹配数),时间复杂度( O(n^3) ),适合n≤100的场景。
解法对比
| 解法 | 适用场景 | 时间复杂度范围 |
|---|---|---|
| 暴力枚举 | n≤5,集合元素极少 | ( O(k_1k_2...*k_n) ) |
| 回溯剪枝 | n≤15,目标元素无重复 | 远低于暴力枚举,依赖剪枝效率 |
| 状态压缩DP | n≤20 | ( O(n2^nk) ) |
| 二分图计数 | n≤100 | ( O(n^3) )(高斯消元) |
示例验证
以题目中的输入为例:
- 集合列表:
[[1,2,3,7], [1,2,4,5], [1,3,5,6], [4,5,6]] - 目标集合:
{1,2,3,4}
通过回溯或DP计算,最终得到可行方案共3种:
- 集合1选1,集合2选2,集合3选3,集合4选4
- 集合1选2,集合2选1,集合3选3,集合4选4
- 集合1选3,集合2选2,集合3选1,集合4选4
内容的提问来源于stack exchange,提问作者Torque
相关产品推荐
相关产品推荐

