假设你有一堆集合,而每个集合都有几个子集。
Set1 = {(香蕉,菠萝,橙子),(苹果,羽衣甘蓝,黄瓜),(洋葱,大蒜)}
Set2 = {(香蕉,黄瓜,大蒜),(牛油果,番茄)}
...
SetN = {...}
现在的目标是从每个集合中选择一个子集,而每个子集必须与任何其他所选子集无冲突。对于这个玩具大小的例子,一个可能的解决方案是选择(香蕉,菠萝,橙色)(来自Set1)和(鳄梨,番茄)(来自Set2)。
如果会选择Set1和Set2的第一个子集,则会发生冲突,因为香蕉将包含在两个子集中(这是不可能的,因为它只存在一次)。
即使有很多算法,我也无法选择合适的算法。我在某种程度上陷入困境,并希望得到针对以下问题的答案:
1)如何找到合适的算法并以可以通过算法处理的方式表示这个问题?
2)这个玩具尺寸示例的可能解决方案可能如何(任何语言都很好,我只是想得到这个想法)。
Edit1:我也在考虑模拟退火(返回一个可能的解决方案)。这可能是有意义的,以最小化例如选择集合的总成本。但是,我无法弄清楚如何进行适当的问题描述,将“冲突”考虑在内。
答案 0 :(得分:8)
此问题可以表述为generalized exact cover problem。
为每组设置(Set1,Set2等)创建一个新原子,并将输入转换为如下所示的实例:
{Set1, banana, pineapple, orange}
{Set1, apple, kale, cucumber}
{Set1, onion, garlic}
{Set2, banana, cucumber, garlic}
{Set2, avocado, tomato}
...
使Set*
个原子成为主要原因(仅覆盖一次),其他原子成为次要原则(最多覆盖一次)。然后你可以用Knuth算法X的推广来解决它。
答案 1 :(得分:4)
查看集合列表,我有一个有多个入口的迷宫图像。该任务类似于从顶部到底部跟踪没有子集交叉的路径。 Haskell中的示例选择所有入口,并尝试每条路径,返回成功的路径。
我对代码如何工作的理解(算法):
对于第一组中的每个子集,选择下一组中的每个子集,其中该子集与累积结果中的每个子集的交集为空。如果没有符合条件的子集,则打破循环的应变。如果没有可供选择的集合,则返回该结果。为所有选择的子集(以及相应的累积结果)递归调用函数。
import Data.List (intersect)
import Control.Monad (guard)
sets = [[["banana", "pineapple", "orange"], ["apple", "kale", "cucumber"], ["onion", "garlic"]]
,[["banana", "cucumber", "garlic"], ["avocado", "tomato"]]]
solve sets = solve' sets [] where
solve' [] result = [result]
solve' (set:rest) result = do
subset <- set
guard (all null (map (intersect subset) result))
solve' rest (result ++ [subset])
输出:
*Main> solve sets
[[["banana","pineapple","orange"],["avocado","tomato"]]
,[["apple","kale","cucumber"],["avocado","tomato"]]
,[["onion","garlic"],["avocado","tomato"]]]