zoukankan      html  css  js  c++  java
  • [饭后算法系列] 最大不重叠的集合覆盖问题

    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) 

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

  • 相关阅读:
    shell脚本比较字符串相等
    从自身的经历讨论手工测试与自动化测试
    读《Linux Shell脚本攻略》(第2版) 一遍、二遍体会
    也许开发需要的只是一份简单明了的表格
    linux 命令:tr 的简单使用
    docker的数据持久化
    docker基础操作
    centos7 docker镜像源设置
    DockerUI(图形化管理)
    Docker 常用命令
  • 原文地址:https://www.cnblogs.com/testview/p/3623681.html
Copyright © 2011-2022 走看看