解决“玩具匹配难题”的最佳算法是什么?

时间:2019-06-05 08:04:45

标签: algorithm

想象一下这样的难题: 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型平板, 解决该问题的最佳算法是什么?答案的时间复杂度是多少?

2 个答案:

答案 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,这使其成为一个相当大的指数。但这也取决于零件向量中条目的大小和模板向量中条目的大小(所需的更少的盘子->更好的时间复杂度,所需的更多盘子->不好的时间复杂度)。

因此时间复杂度不是很好。对于较大的问题,这将花费非常长的时间,但是对于诸如您所提问的小问题,它应该起作用。