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


    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) 

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

  • 相关阅读:
    Go标准库Context
    事务并发处理: DB+ORM+逻辑代码
    日志:slf4j+log4j+maven配置
    Shiro workshop
    JSP Workshop
    sql records
    Java内存模型(JMM)
    Application, JDBC, 数据库连接池, Session, 数据库的关系
    Java位操作全面总结
    Effective Java总结
  • 原文地址:https://www.cnblogs.com/testview/p/3623681.html
Copyright © 2020-2023  润新知