如何最有效地分配房间?

时间:2013-04-17 13:53:39

标签: algorithm language-agnostic

故事:

我们公司即将外出。对于我们住在度假村,我们每两个同事都会共用一个房间。我们的行政助理收集了我们与谁共用房间的偏好,现在她必须决定如何安排房间以尽量减少所需的房间数量。每个人都将安排与他或她想要的人分享一个房间。例如,只有同事,艾伦希望与鲍勃或克里斯分享一个房间,鲍勃想与克里斯分享,克里斯想与艾伦分享;然后唯一的结果将是:艾伦和克里斯共用一个房间,鲍勃独自使用一个房间,总共需要2个房间。

问题:

将故事简化为算法问题(尽管可能不是最好的简化):我们在图中有一些节点,并且节点相互连接。我们只关心双向连接的节点,所以现在我们有一个不定向的图。如何将无向图中的节点划分为组,以便1)任何组最多包含2个节点,2)如果一个组包含2个节点,则节点连接,3)组的数量最小化。

算法:

我头脑中的问题是贪婪地解决问题。在排列的每个步骤中,只需删除一个孤立的节点或两个节点,以便最大化图形中保留的边数。通过反复这样做,我们最终会找到解决方案。

请以最佳方式解决问题(我不是在寻找尝试所有组合的方法)或证明上述贪婪算法是最佳的。

2 个答案:

答案 0 :(得分:3)

您要解决的问题是在图表中找到maximum matching。这意味着找到不共享顶点的最大边数。在您的情况下,这些边缘将对应于共享房间,其余顶点将是单个房间。

可以使用多项式时间中的Blossom algorithm找到最大匹配。

答案 1 :(得分:0)

这是Haskell中粗暴的东西。功能“配对”列出了具有共同偏好的所有对,以及没有共同伙伴的人(与“”配对)。函数“选择”返回配对列表中的对。如果一对中的两个人也与另一个(相同的)第三人配对,则“选择”从该对列表的其余部分中移除这两个人,以及因此清空的对。所需的房间数量等于最终清单的长度。

输出(有更多不同的例子可以测试):

*Main> choose graph
[["Chris","Allen"],["Bob","Isaak"]]

*Main> choose graph1
[["Allen","Chris"],["Bob",""],["Dave",""],["Chris","Max"]] --four rooms
  would be needed, although Chris appears in two pairs (..figured they can 
  decide later who stays where.)

*Main> choose graph2 --example given by Dante is not a Geek
[["Allen","Chris"],["Bob",""]]

代码:

import Data.List (group, sort, delete)

graph = [("Chris",["Isaak","Bob","Allen"]) --(person,preferences)
        ,("Allen",["Chris","Bob"])
        ,("Bob",["Allen","Chris","Isaak"])
        ,("Isaak",["Bob","Chris"])]

graph1 = [("Allen",["Bob","Chris"]), ("Bob",["Chris"]), ("Dave",[])
         ,("Chris",["Allen", "Max"]), ("Max", ["Chris"])]

graph2 = [("Allen",["Bob","Chris"]), ("Bob",["Chris"]), ("Chris",["Allen"])]

pairs graph = pairs' graph [] where
  pairs' []                 result = concat result
  pairs' (x@(person1,_):xs) result
    | null test = if elem [[person1, ""]] result 
                     then pairs' xs result
                     else pairs' xs ([[person1,""]]:result)
    | otherwise = 
        pairs' xs ((filter (\[x,y] -> notElem [y,x] (concat result)) test):result)
   where isMutual a b = elem (fst a) (snd b) && elem (fst b) (snd a)
         test = foldr comb [] graph
         comb a@(person2,_) b = 
           if isMutual a x then [person1,person2]:b else b

choose graph = comb paired [] where
  paired = pairs graph
  comb []             result = filter (/=["",""]) result
  comb (x@[p1,p2]:xs) result 
    | x == ["",""] = comb xs result
    | test         = 
        comb (map delete' xs) (x:map delete' result)
    | otherwise    = comb xs (x:result)
   where delete' [x,y] = if elem x [p1,p2] then ["",y]
                            else if elem y [p1,p2] then [x,""] 
                            else [x,y]
         test = if not . null . filter ((>=2) . length) . group 
                       . sort . map (delete p2 . delete p1) 
                       . filter (\y -> y /= x && (elem p1 y || elem p2 y)) $ paired
                   then True
                   else False