动态规划能否解决二维列表最小化distinct值扁平化问题?求最优解法
最小化扁平化数组的distinct元素数量解法
问题明确
给定二维列表,从每个子数组选一个元素组成一维数组,目标是让结果里的不同元素数量最少。示例如下:
- 输入
[[1,2],[1,2],[1,3],[5,4],[4,6]],最优输出[1,1,1,4,4](仅含2种不同元素) - 输入
[[1,2,3],[1,3],[1,8],[5,6],[6,5],[4]],最优输出[1,1,1,5,5,4]或[1,1,1,6,6,4](仅含3种不同元素) - 反例输入
[[1,2],[1,2],[1,2],[1,3],[3,4],[3,4],[4,5,9]],单纯统计元素频次的贪心策略会得到[1,1,1,1,3,3,4](含3种元素),但最优解是[1,1,1,1,4,4,4](仅含2种元素)——这说明只看频次的方法不可靠。
核心本质:最小击中集问题
这个问题等价于寻找最小元素集合,要求每个子数组中至少有一个元素属于该集合。找到这个集合后,从每个子数组中挑选集合内的元素,就能得到distinct数量最少的结果数组。
分场景解决
场景1:所有子数组长度≤2(多项式可解)
此时问题可转化为图的顶点覆盖问题,能通过多项式时间算法解决:
- 将每个元素视为图的顶点
- 若两个元素出现在同一个子数组中,则在它们之间连一条边
- 求解该图的最小顶点覆盖,这个覆盖集合就是目标最小元素集合
- 对每个子数组,选择集合中的任意一个元素即可
场景2:存在子数组长度>2(NP难问题)
当子数组长度超过2时,最小击中集是NP难问题,需根据数据规模选择解法:
回溯法(精确解,适合小规模数据)
按元素频次降序排序优化搜索效率,从小到大尝试不同大小的元素子集,找到第一个能覆盖所有子数组的最小子集:
def find_min_hitting_set(arrays): # 统计元素频次并降序排序,减少搜索次数 freq = {} for sub in arrays: for num in sub: freq[num] = freq.get(num, 0) + 1 sorted_elements = sorted(freq.keys(), key=lambda x: -freq[x]) # 从最小子集大小开始尝试 for size in range(1, len(sorted_elements)+1): from itertools import combinations for candidate in combinations(sorted_elements, size): # 检查当前子集是否覆盖所有子数组 valid = True for sub in arrays: if not any(num in candidate for num in sub): valid = False break if valid: return set(candidate) return set() def flatten_min_distinct(arrays): hitting_set = find_min_hitting_set(arrays) result = [] for sub in arrays: # 取子数组中第一个属于击中集的元素 for num in sub: if num in hitting_set: result.append(num) break return result # 测试反例 test_case = [[1,2],[1,2],[1,2],[1,3],[3,4],[3,4],[4,5,9]] print(flatten_min_distinct(test_case)) # 输出 [1,1,1,1,4,4,4]
贪心近似算法(适合大规模数据)
每次选择能覆盖最多未被覆盖子数组的元素,重复操作直到所有子数组被覆盖。该方法的近似比为O(log n),能快速得到接近最优的结果:
def greedy_hitting_set(arrays): # 复制子数组为集合形式,避免修改原数据 remaining = [set(sub) for sub in arrays] hitting_set = set() while remaining: # 统计每个元素能覆盖的剩余子数组数量 count = {} for sub in remaining: for num in sub: count[num] = count.get(num, 0) + 1 # 选择覆盖数量最多的元素 best_num = max(count, key=count.get) hitting_set.add(best_num) # 移除被该元素覆盖的子数组 remaining = [sub for sub in remaining if best_num not in sub] return hitting_set
为什么频次统计法失效?
反例中,元素1可覆盖前4个子数组,元素4可覆盖后3个子数组,两者组合仅需2个元素;而元素3虽然频次高,但只能覆盖第4-6个子数组,和元素1组合需要3个元素。单纯选取高频元素会陷入局部最优,无法得到全局最优解。
注:若有比动态规划更优的解法,欢迎提出。
内容的提问来源于stack exchange,提问作者raspace
相关产品推荐
相关产品推荐

