查找成员集是否涵盖任何集合

时间:2013-05-31 06:02:12

标签: algorithm set

[如果这映射到已知问题,请告诉我]

我有n套不同的尺码。集合中的每个元素都是唯一的。每个元素最多可以出现在两个不同的集合中。

我想对这些集合执行操作,但避免重复或丢失任何元素。 问题:找出应删除所有这些n集的内容,因为它们被其他集覆盖。

E.g。 [A,B,C]; [一个];并[b]。删除[a],[b],因为两者都被第一个覆盖。

E.g。 [A,B,C]; [一个];并[b]; [光盘]。删除[a,b,c],因为所有三个元素都被剩余的集合覆盖。 注意:此处[a],[b]单独无效答案,因为'c'正在重复。类似地,[a],[b],[c,d]无效答案,因为如果删除则会遗漏'd'。

4 个答案:

答案 0 :(得分:3)

我认为这是Exact Cover problem。最后一个约束 - 每个元素最多在两个集合中 - 在我看来并不能从根本上改变问题(虽然我可能很容易对此错误)。维基百科网页包含各种算法方法的很好的总结。选择的算法似乎是Dancing Links

答案 1 :(得分:3)

我认为这是一个2-sat problem的案例,可以使用基于methodTarjan's algorithm在线性时间内解决。

  1. 为每个集合制作一个变量Ai。当且仅当包括i时,Ai才是真实的。
  2. 对于出现在单个集合中的每个元素,添加一个Ai = 1
  3. 的子句
  4. 对于出现在2组i和j中的每个元素,添加子句(Ai&& ~Aj)|| (~Au&& Aj)。这些条款意味着必须出现Ai和Aj中的一个。
  5. 现在,您可以使用标准的2-sat算法来解决这个问题,以确定是否可能无法实现这一目标,或者如果可能的话,可以进行令人满意的分配。

    对于具有V组和N个元素的情况,您将具有V变量和最多2N个子句,因此Tarjan的算法将具有复杂度O(V + 2N)。

答案 2 :(得分:1)

由于集合中的元素可以出现在不超过两个集合中,因此集合之间存在相当简单的连接,可以显示为图形,下面显示了两个示例。一个示例使用红线表示边缘,另一个使用黑线表示边缘。

Graph Example

以上显示这些集合可以分为三组。

  1. 设置所有元素出现两次的位置。可能会删除这些集合和/或可以删除​​包含这些元素的集合。
  2. 设置一个或多个元素出现两次的位置。出现两次的元素可能会链接到可以删除的集合。
  3. 设置没有元素出现两次的位置。这些集合可以忽略。
  4. 如果所有集合都在第1组或第3组中,会发生什么事情并不是很清楚。但似乎有一个相当简单的标准允许快速删除集合,而psudocode看起来像这样:

    for each set in group2:
        for each element that appears twice in that set:
            if the other set that contains that element is in group1:
                remove the other set
    

    然后,元素数量的性能是线性的。

答案 3 :(得分:0)

我试图找到要包含而不是删除的集合。像这样的东西?

(1)元素列表及其所在集合的索引
(2)使用具有仅出现在其中的元素的集合的索引来填充答案列表
(3)梳理(1)中的地图,如果元素的集合索引不在答案列表中,则在答案中添加元素所在的最小集合的索引。

Haskell代码:

import Data.List (nub, minimumBy, intersect)

sets = [["a","b","c","e"],["a","b","d"],["a","c","e"]]
lengths = map length sets

--List elements and the indexes of sets they are in

mapped = foldr map [] (nub . concat $ sets) where
  map a b = comb (a,[]) sets 0 : b
  comb result   []     _     = result
  comb (a,list) (x:xs) index | elem a x  = comb (a,index:list) xs (index + 1)
                             | otherwise = comb (a,list) xs (index + 1)

--List indexes of sets that have elements that appear only in them

haveUnique = map (head . snd)
           . filter (\(element,list) -> null . drop 1 $ list) 
           $ mapped

--Comb the map and if an element's set-index is not in the answer list,
--add to the answer the index of the smallest set that element is in.

answer = foldr comb haveUnique mapped where
  comb (a,list) b 
    | not . null . intersect list $ b = b
    | otherwise                       = 
        minimumBy (\setIndexA setIndexB -> 
                    compare (lengths!!setIndexA) (lengths!!setIndexB)) list : b

输出:

*Main> sets
[["a","b","c","e"],["a","b","d"],["a","c","e"]]

*Main> mapped
[("a",[2,1,0]),("b",[1,0]),("c",[2,0]),("e",[2,0]),("d",[1])]

*Main> haveUnique
[1]

*Main> answer
[2,1]