想象一下这样的难题: puzzle
我有几种形状,例如:
10个圈子
8个三角形
9个正方形
我也有一些盘子可以放置形状,例如:
图板A:2个圆孔,3个三角形孔,1个方孔
板B:1个圆孔,0个三角形孔,3个方孔
板C:2个圆孔,2个三角形孔,2个方孔
我想找到最小数量的板来放置所有形状(板不需要完全填充)
例如:
我可以选择6个板块[A,A,A,B,B,C],并且可以插入所有形状
但是我也可以选择[A,A,C,C,C],这也可以,
所以这个问题的答案是:5
如果此问题普遍涉及N型形状和M型平板, 解决该问题的最佳算法是什么?答案的时间复杂度是多少?
答案 0 :(得分:1)
这个问题是一个NP难题,一旦意识到从polynomial time reduction到这个问题有一个非常简单的bin packing problem,就更容易看到它。
我建议您使用integer linear programming技术来解决它。
可以解决以下问题的ILP:
// Data
Shapes // array of integers of size n, contains the number of each shape to fit
Plates // 2D array of size n * m, Plates[i][j] represents the number of shape of type i
// that fit on a plate of type j
// Decision variables
X // array of integer of size m, will represent the number of plates of each type to use
// Constraints
For all j in 1 .. m, X[j] >= 0 // number of plates cannot be negative
For all i in 1 .. n, sum(j in 1..m) Plates[i][j] * X[j] >= Shapes[i] // all shapes must fit
Objective function:
minimize sum(j in 1..n) X[j]
在OPL中编写伪代码,将其输入到linear programming solver中,鉴于此问题与bin打包的相似性,您应该可以很快地找到解决方案。
编辑:如果您不想在学习LP基础知识,OPL,LP求解器等方面遇到麻烦,那么解决此问题的最佳和最简单方法将是旧的branch and bound实现这个问题。分支定界是一种非常简单且功能强大的算法,可用于解决各种各样的问题....必不可少的内容。
答案 1 :(得分:0)
我认为应该使用动态编程来解决此问题。
这是伪代码的解决方案(我尚未测试过,但我认为它应该可以工作):
parts = the number of shapes we want to fit as a vector
plates = the of plates we can use as a matrix (vector of vector)
function findSolution(parts, usedPlates):
if parts < 0: //all elements < 0
return usedPlates;
else:
bestSolution = null //or anything that shows that there is no solution yet
for X in plates:
if (parts > 0 on any index where X is > 0): //prevents an infinite loop (or stack overflow because of the recursion) that would occur using only e.g. the plate B from your question
used = findParts(parts - X, used.add(X)); //elementwise subtraction; recursion
if (used.length < best.length):
//the solution is better than the current best one
best = used;
//return the best solution that was found
return best
使用问题中的值,初始变量为:
parts = [10, 8, 9]
plates = [[2, 3, 1], [1, 0, 3], [2, 2, 2]]
,您将启动以下功能:
solution = findSolution(parts /*= [10, 8, 9]*/, new empty list);
//solution would probably be [A, A, C, C, C], but also [C, C, C, C, C] would be possible (but in every case the solution has the optimal length of 5)
使用此算法,您可以使用递归将问题划分为较小的问题(大多数动态编程算法都这样做)。
时间复杂度不是很好,因为您必须搜索所有可能的解决方案。 根据{{3}},时间复杂度应类似于:O(n ^(log_b(a)))其中n = a =使用的板数(在您的示例3中)。 b(对数的底)不能在这里计算(或者至少我不知道如何),但是我认为它将接近1,这使其成为一个相当大的指数。但这也取决于零件向量中条目的大小和模板向量中条目的大小(所需的更少的盘子->更好的时间复杂度,所需的更多盘子->不好的时间复杂度)。
因此时间复杂度不是很好。对于较大的问题,这将花费非常长的时间,但是对于诸如您所提问的小问题,它应该起作用。