今天有朋友问我关于转让问题的问题。我找到了一个非常简单的解决方案,但我觉得它可以变得更简单,更快捷。 感谢您的帮助。
问题: 假设我有 N 人,我需要将它们分配到 M 建筑物中,每个建筑物都可以容纳 K 人。并非所有人都愿意相互生活,所以我有一个N * N细胞矩阵和一个标志着愿意相互生活的人的1。如果一个单元格包含1,则意味着我和J可以共存。显然,矩阵在主对角线周围是对称的。
我的解决方案如下(伪代码):
int[] Match(int[] people, int[][] pairs, int numBuildings, int buildingsSize)
{
int[] freePeople = findFreePeople(people);
if(freePeople) = 0
{
return people;
}
foreach(int person in freePeople)
{
for(int buildingIndex=0 to numBuildings)
{
if( CheckIfPersonFitsInBuilding(...) )
{
int[] tempPeople = people.Copy();
tempPeople[person] = buildingIndex;
int[] result = Match(tempPeople,pairs,numBuildings,buildingsSize);
if(result != null)
{
return result;
}
}
}
}
return null;
}
我只是使用递归来涵盖所有可能的安排。我觉得这可以大大优化,我不是在谈论启发式方法,而是一种复杂度较低的解决方案。
我认为这可能与stable marriage problem有关,但我不确定。
答案 0 :(得分:25)
通过减少decomposing a graph into cliques( clique cover problem )的NP完全问题,已知这个问题是NP难题。
首先,一些术语和讨论。图中的clique是一组k个不同的节点,使得每个节点连接到clique中的每个其他节点。图中的independent set是一组k个不同的节点,因此没有两个节点彼此连接。如果您获取任何图形G,然后在缺少边缘时引入边缘并移除先前存在的所有边缘,则会得到原始图形的complement graph。这意味着在初始图中找到一个团的问题等同于在补图中找到一个独立的集。
这个有趣的原因是,在你所描述的问题中,你会得到一个人物图表,每个边缘都表示“这两个人不能被安置在一起”。因此,您可以将您所描述的问题视为试图找到将图形分解为k个子图的方法,每个子图都是一个独立的集合。如果您构建此图的补充,您将获得所有可以放在一起的人的图表。在这种情况下,您希望将组分成k组,这些组都是派系。
众所周知,以下问题是NP完全的:
给定一个图形和一个数字k,你能否将图形中的节点分成k个集团?
我们可以在多项式时间内将此问题减少到您的问题,表明您的问题是NP难的。构造很简单 - 给定初始图G和数k,首先构造G的补数。这意味着如果你可以将补数分解为k个独立集,那么原始图G可以分解为k个集团(因为集团与独立集之间的二元性)。现在,使用这个新图并将其转换为一组人,每个节点一个,如果他们的节点在原始图中连接,则两个人不能放在同一个房间。现在,有一种方法可以将这些人分配到k个房屋中,如果G的补充可以分解为k个独立集合,如果G可以分解为k个集团。因此,已知的NP-complete clique cover问题可以在多项式时间内减少到你的问题,所以你的问题是NP难的。要确保任何独立集合适合房屋,只需将每个房间的最大容量设置为n,即图表中的节点数量。这允许任何独立的集合被安置在任何房间。
由于你的问题是NP难的,除非P = NP,否则不能有多项式时间解决方案。因此,就像任何人都知道的那样,它没有多项式时间算法,你的回溯递归可能是最佳解决方案。
希望这有帮助!
答案 1 :(得分:13)
templatetypedef给出a very nice proof为什么问题是NP难的并且没有(已知的)多项式时间解决方案。
然而,正如许多NP难问题一样,这并不意味着你无法在实践中有效地解决它,或者你无法改进蛮力解决方案。
特别要考虑constraint satisfaction problems。它们是一类更为通用的问题,它们准确描述了您要解决的问题:给定一组约束条件,满足所有约束条件的值是什么?
这本书 AIMA 有a very nice chapter关于如何解决这些类型的问题。我建议你阅读一下,因为那里有很多很好的信息,因为这本书是为本科生设计的,所以很容易理解。我会在这里给你一些重要的想法:
主要问题是:
以下是两种启发式方法:
对于MRV启发式,通过选择对其他学生具有最多约束的学生来打破联系。这些启发式背后的想法是,您希望选择最有可能成功的搜索路径(LCV)。但是考虑到特定的搜索路径,您希望尽早失败,以减少在该路径上花费的时间(MRV)。
此外,您应该使用更先进的搜索算法,例如AC-3,而不是基本前向检查的天真递归。
在许多软件工程应用程序中看到约束满足问题是如此常见,已经有大量的开放式库可以解决这些问题,从而有效地解决它们。当然,这是因为您尝试解决的问题是实际应用程序,而不是各种类型的家庭作业。
答案 2 :(得分:4)
解决这些问题的一个好方法是使用有限域的约束求解器。
例如,使用GNU-Prolog:
solve(Out):-
People = [Jon, Mary, Alice, Smith, Dick, George, Betty, Lucy],
fd_domain(People, 0, 3), % people lives in buildings 0 to 3.
fd_atmost(4, People, 0), % at most, 4 people may live in building 0
fd_atmost(3, People, 1), % at most, 3 people may live in building 1
fd_atmost(2, People, 2), % etc.
fd_atmost(1, People, 3),
Jon #\= Mary, % Jon hates Mary
Alice #\= Smith, % etc.
Betty #\= Lucy,
Jon #\= Alice,
Dick #\= George,
fd_labeling(People), % iterate until an acceptable combination is found.
People = Out.
:- solve(O), write(O), nl.
从复杂的角度来看,这仍然是NP完全的。但约束求解器可能会重新排序在标记步骤中执行赋值的方式,以便尽早找到死角,从而产生较小的搜索树。