给定N组数字,每组数字组内数字之间互不相同,组间数字可能相同。这些数字都是正整数,现在从这N组数字中选择尽量多的数字,使得选出来的数字满足以下条件:
- 每组只选出一个数字
- 选出来的数字互不相同
要求:使选出来的数字之和尽量大。
暴力想法显而易见,但是有没有更完美的方法呢?
import numpy as np
def generate_problem():
group_count = np.random.randint(5, 6)
groups = []
for i in range(group_count):
group_size = np.random.randint(2, 5)
groups.append(sorted(list(set(np.random.randint(1, 10, group_size))), reverse=True))
return groups
def brute_force(groups):
groups = [i for i in groups if i]
if not groups:
return 0
max_value = max(i[0] for i in groups)
big_groups = [i for i in groups if i[0] == max_value]
ans = 0
for big in big_groups:
next_groups = [j[1:] if j[0] == max_value else j for j in groups if j != big]
s = brute_force(next_groups) + max_value
ans = max(ans, s)
return ans
def main():
for i in range(100):
p = generate_problem()
s = brute_force(p)
print(p)
print(s)
print('=' * 10)
main()
这个问题等价于:
将全部数字合并,去重,排序得到一个数组A,下面构建一个矩阵,数组A为列名,数组每行为每组的数字,如果存在则用此值代替,如果不存在,用0代替。这样就得到一个稀疏矩阵。
此题转化为:在这个矩阵中寻找最优组合,要求每行、每列只能选择一个数字,选定若干数字使最终结果之和最大。
此问题其实是二分图。
将全部数字合并,去重之后放在右面,将全部数组放在左边。如果左边的数组X包含右边的数字Y,则在它们之间连一条线。左边的结点和右边的结点都只能使用一次,求最大匹配(边权之和最大)。
在本题中,最终的二分图模型左右结点个数可能不等,所以无法找到完美匹配。
留一道更难的题:
给定一个$N imes M$的矩阵,每行每列最多只能选择一个元素,最终选出来的元素不可重复,要使选出来的数字之和尽量大。