[饭后算法系列] 最大不重叠的集合覆盖问题

1. 问题

给定一组集合S1, S2, ..., Sn,其满足:对任意一个集合Si,存在Sj (j!=i) 使得Si与Sj相交

要求: 从S1, S2, ..., Sn选择k个互不相交的集合,使得这k个集合的并集包含的元素最多

2. 分析

集合覆盖属于NP完全问题多项式内不能解, 时间复杂度最好为O(2^n)

因此, 我们可以做一些多项式时间的预处理:

1. 做预处理, 得到B(Si, Sj) = true/false, 表示两个集合是否相交

2. 做预处理, 得到|Si| = Ni, 表示集合的元素数

暴力解法如下:

1. 取出{S1, S2, ..., Sn}的所有子集, 共O(2^n)种情况

2. 计算该子集内部是否有两个元素有交集, 这是一个双重循环, 时间复杂度O(n^2)

因此暴力法的时间复杂度为O(2^n * n^2)

3. 动态规划

对于{S1, ..., Sn}的一个子集{Si1, Si2, ..., Sik}, 要计算它的覆盖结果R(Si1, Si2, ..., Sik):

1. 如果{Si1, Si2, ... Sik}是两两不相交的, 则R(Si1, Si2, ..., Sik) = R(Si1, Si2, ..., Si(k-1)) + |Sik|

2. 如果{Si1, Si2, ... Sik}内部有交集, 则R(Si1, Si2, ..., Sik) = 0

第2步有两个可能:

2.1 {Si1, Si2, ... Si(k-1)}内部已经有交集了, 即R(Si1, Si2, ..., Si(k-1)) = 0

2.2 R(Si1, Si2, ..., Si(k-1)) != 0, 但Sik和{Si1, Si2, ... Si(k-1)}中的某个S有交集

算法的主体:

 1 result = {S1, ..., Sn}的一个子集, max = 该子集并集的元素数
 2 初始状态result = 空集, max = 0
 3 for s = S1 to Sn:  # 循环单个元素
 4   R(s) = |s|
 5   if R(s) > max:
 6     max = R(s), result = s
 7 
 8 # 循环所有子集
 9 for size = 2 to n:
10   for A 为 {S1, ..., Sn}长度为size的子集:
11     last(A) = A的最后一个元素
12     pre(A) = A - last(A) 即A去除最后一个元素
13     R(A) = R(pre(A)) + |last(A)| if R(pre(A)) > 0 and last(A)和pre(A)中的元素都不相交 (*)
14     R(A) = 0 if 上述条件不满足
15     if R(A) > max:
16       max = R(A), result = A

算法的第9, 10行一共迭代了{S1, S2, ..., Sn}的所有子集, 共O(2^n)种情况

算法的第13行(打*号)中, 需要通过一次循环进行相交的判定, 需要O(n)时间

因此动态规划法的总体时间复杂度为 O(2^n * n) 

关键字: 算法, 集合覆盖, 动态规划

原文地址:https://www.cnblogs.com/testview/p/3623681.html