给定一组项目(大小在1到100之间)和多个箱子(1到15个)。每个项目都有一个箱子集可以分配项目,以及哪个箱子的偏好排序最好,仅次于此,等等。项目也有一个自然顺序,下面用命名表示,例如item2之前的item1。每个箱子的容量在1到5之间(每个物品具有相同的重量,即1。)
示例输入可以是三个箱子和六个物品( - 表示箱子不在物品的可用设置中,即不能用它包装):
| bin1 bin2 bin3 | bin1 bin2 bin3 ------------------------ ---------------------------- item1 | 1 2 - capacity | 4 4 5 item2 | - 1 2 item3 | 2 1 3 item4 | 1 2 3 item5 | 1 - 2 item6 | 1 2 3
目标是(按照每个目标在发生冲突时完全覆盖任何较低目标的顺序,例如,无论使用多少个箱子或忽略偏好,包装五个项目总是好于四个):
因此上面的输入将打包为:
| bin1 bin2 bin3 ------------------------ item1 | x item2 | x item3 | x item4 | x item5 | x item6 | x
接下来的问题是阅读/评论什么来帮助我提出解决这个问题的算法思路,使用第一段的输入大小和几秒钟的时间约束,即不是暴力(或者至少我迄今为止所构想的任何蛮力。)我正在使用Ruby和C,但语言在这个阶段的树林里磕磕绊绊并不过分。
我会感激任何阅读建议,关于算法组合的想法,或只是澄清问题陈述的想法......
更新1
虽然不太清楚,虽然有许多算法涵盖了这一部分,但我的困难在于找到(或者可能识别)处理所有标准的信息,特别是在容量过剩和冲突时最小化使用的箱数item-to-bin集和项目首选项,希望在以下示例中更清楚地显示:
| bin1 bin2 bin3 | bin1 bin2 bin3 ------------------------ ---------------------------- item1 | 1 2 3 capacity | 3 2 3 item2 | 1 2 3 item3 | - 1 2
虽然bin1是最受欢迎的,但是item3根本不能放在其中,而bin2是所有项目的下一个最优选项目,它只能容纳三个项目中的两个。所以正确的赋值集(x)实际上是最不喜欢的bin:
| bin1 bin2 bin3 ------------------------ item1 | x item2 | x item3 | x
更新2 我使用有关目标如何关联的信息重新描述了描述并删除了bin优先级的变量,因为它只会使得找到答案的可能性降低,并且可以在我正在处理的系统中的其他地方解决。
答案 0 :(得分:1)
这让我想起曾经将医学院毕业生安置在住院医师计划中的"Match" algorithm。如果您将学生这样的项目,他们的箱子偏好(如等级列表)和像医院这样的箱子一起对待会怎么样?
基本上,您浏览项目列表,并为每个项目找到它最喜欢的bin。用箱子检查:你有这个项目的空间吗?如果没有,你比你现有的任何物品更喜欢它吗? 如果不是,请将此bin从项目列表中移除,然后移至项目的下一个选项 如果是,请将此项目放入垃圾箱,并将移位的项目(如果有)放回不匹配的池中。
您的问题与居住匹配之间的区别在于您不会预先修复垃圾箱的偏好。相反,您将使用一个规则,该规则更喜欢使垃圾箱最接近100%满的物品。
我唯一担心的是这种修改可能会使算法不稳定。但它是一个如此简单的算法,它可能值得尝试。
答案 1 :(得分:1)
这是一个二分匹配问题,可以在多项式时间内求解。
http://en.wikipedia.org/wiki/Matching_(graph_theory)#Maximum_matchings_in_bipartite_graphs
答案 2 :(得分:1)
假设有n个项目和b个bin,每个bin的大小为s。您添加的约束的排序实际上简化了问题。
它们特别指的是我们应该总是选择项目1,2,...,m,以获得最大的m <= n,这将适合分配的数量的箱子(因为选择较小的数量必然会产生更糟的解决方案按规则1)。物品将按此顺序装在箱子中,可能还有一些箱子未完全填满(因为在箱子内或箱子之间重新安排物品会因规则2而产生更糟糕的解决方案)。有两种情况:
在这种情况下,紧密的包装箱可能会留下0 <0的最终块。 e <= b的箱子完全空了。在这种情况下,丢弃那些最后的空箱并继续进行(因为使用更多的箱会因规则3而产生更糟的解决方案)。在任何情况下,调用最后剩余的垃圾箱数r。 (r = b - e。)
我们现在确切地知道我们将使用哪些物品和哪些箱子。我们也知道必须包装物品的顺序。由于排序约束,我们可以将关于哪些箱子未完全填充的决定视为如何将“start-next-bin”指令注入有序列表1,2,...... n的问题。项目。我们最多可以注入r-1条指令。
使用动态编程可以在O(nrs)时间内解决此问题。基本上我们计算函数:
f(i,j,k)=第一个i项占据前j个框的最佳解的得分,第j个框中恰好有k个项。
重现是:
f(i, j, 0) = max(f(i, j-1, k)) over all 0 <= k <= s
f(i, j, k > 0) = f(i-1, j, k-1) + q(i, j)
其中q(i,j)是将项目i分配给方框j的质量分数。 (正如我在你的帖子评论中所提到的,你需要决定某种方式将任何项目i的位置分配到任何方框j中,大概是基于我的偏好得到满足。如果它更容易使用“错误“值不是质量值,只需将max()
更改为min()
,将-infinity
边界值更改为infinity
。)
第一个等式表示最右边的bin为空的第一个i项的解的最佳分数等于通过考虑没有该bin的第一个i项的每个解的最佳分数。这些候选解决方案包括前一个bin可以打包的所有方式,包括将其留空。
第二个等式表示,对于最右边的箱子不为空的第一个i项目的最佳分数,只需添加质量分数,将最后一个项目放入最佳分数,以便将第一个i-1项目放在同一个项目中箱数。
边界条件是:
f(0, 0, 0) = 0
f(i, 0, k) = -infinity for all other i and k
在计算每个0&lt; = i&lt; = n,0&lt; = j&lt; = r和0&lt; = k&lt; = s并且存储它们的f(i,j,k)的值之后在表格中,f(n,r,s)将给出最终解的最佳分数。虽然这只给出了最大分数,但是通过从末尾追溯f(i,j,k)矩阵可以找到实际的最优解,在每个k = 0步骤寻找前趋状态(即必须导致当前状态的max()
下的替代方案。 (可能会发生max()
下的几个替代方案给出相同的分数,在这种情况下,存在多个最优解,并且可以遵循这些路径中的任何一个来找到其中一个。)