【问题标题】:Algorithm to get best combination获得最佳组合的算法
【发布时间】:2016-09-30 12:27:27
【问题描述】:

我有 ID 为 1, 3, 4, 5, 6, 7 的项目。现在我有如下数据。 每行都有一个 offerId。 Array of Ids 由数组中的ID 组合而成。 DiscountofferId 的值

offerId : Array of Ids     : Discount
o1      : [1]              : 45
o2      : [1 3 4]          : 100
o3      : [3 5]            : 55
o4      : [5]              : 40
o5      : [6]              : 30
o6      : [6 7]            : 20

现在我必须选择所有给我最佳 ID 组合的 offerId,即最大总折扣。

例如在上述情况下:可能的结果可能是:

[o2, o4, o5] 最大折扣为170(100 + 40 + 30)

注意。结果 offerId 应该是 Id 不重复。 o2,o4,o6 的示例是 [1,3,4]、[5]、[6],它们都是不同的。

其他组合可以是: o1, o3, 06 的 id 分别为 [1]、[3,5]、[6,7] 但是总数为 120(45+55+20) 小于 170,如前一种情况。

考虑到每个报价都应包含不同的 Ids,我需要一个算法/代码来帮助我识别 combination of offerIds,这将给出 maximum discount

注意我正在用go 语言编写我的代码。但是任何语言的解决方案/逻辑都会有所帮助。

注意:我希望我能够正确解释我的要求。如果需要任何额外信息,请发表评论。谢谢。

【问题讨论】:

  • 我不确定这是否是问这个问题的正确地方。请不要投反对票。
  • 这看起来并不比 exact cover problem 更容易,但有权重,所以我认为多项式解决方案不太可能。
  • 在这个问题中,如果只有几个 ID(比如说,最多 20 个),我会采用动态编程解决方案:f (s: subset of IDs) = 最大可能折扣对于任何一组提供精确子集s
  • 我已经编辑了我的问题。有时最佳组合不会是一组中的exact cover 。 (注意:我已将 o5 的值更改为 30)
  • 因此,您必须检查所有可能的子集,而不仅仅是整个集合。算法复杂度保持不变。

标签: c++ c algorithm go


【解决方案1】:

这是一个动态规划解决方案,它针对每个可能的 ID 子集,找到折扣最大的优惠组合。 这将是伪代码。

让我们的报价是具有字段offerNumbersetOfItemsdiscount 的结构。 为了实现的目的,我们首先用整数从零到不同的可能项目的数量(比如k)减去一来重新枚举可能的项目。 之后,我们可以用长度为k 的二进制数来表示setOfItems。 例如,如果 k = 6 和 setOfItems = 1011102,则该集合包括项目 5、3、2 和 1,不包括项目 4 和 0,因为位 5、3、2 和1 为 1,位 4 和 0 为 0。

现在让f[s] 成为使用精确设置的s 项目可以获得的最佳折扣。 这里,s 可以是 0 到 2k - 1 之间的任何整数,表示 2k 个可能的子集之一。 此外,让p[s] 成为报价列表,它们一起使我们能够为一组项目s 获得折扣f[s]。 算法如下。

initialize f[0] to zero, p[0] to empty list
initialize f[>0] to minus infinity
initialize bestF to 0, bestP to empty list
for each s from 0 to 2^k - 1:
    for each o in offers:
        if s & o.setOfItems == o.setOfItems:  // o.setOfItems is a subset of s
            if f[s] < f[s - o.setOfItems] + o.discount:  // minus is set subtraction
                f[s] = f[s - o.setOfItems] + o.discount
                p[s] = p[s - o.setOfItems] append o.offerNumber
                if bestF < f[s]:
                    bestF = f[s]
                    bestP = p[s]

之后,bestF 是可能的最佳折扣,bestP 是为我们提供该折扣的报价列表。

复杂度为 O (|offers| * 2k),其中k 是项目总数。

这是另一种渐近相同的实现,但在大多数子集不可达时在实践中可能会更快。 它是“向前”而不是“向后”动态规划。

initialize f[0] to zero, p[0] to empty list
initialize f[>0] to -1
initialize bestF to 0, bestP to empty list
for each s from 0 to 2^k - 1:
    if f[s] >= 0:  // only for reachable s
        if bestF < f[s]:
            bestF = f[s]
            bestP = p[s]
        for each o in offers:
            if s & o.setOfItems == 0:  // s and o.setOfItems don't intersect
                if f[s + o.setOfItems] < f[s] + o.discount:  // plus is set addition
                    f[s + o.setOfItems] = f[s] + o.discount
                    p[s + o.setOfItems] = p[s] append o.offerNumber

【讨论】:

  • 有没有高效的算法来表示setOfItems by a binary number of length k
  • @Jagrati 当然。首先,将连续整数分配给所有项目,从零开始。之后,每个 setOfItems 只是集合中所有索引 i 的总和 2^i。例如,对于集合 {0, 3, 4},二进制表示为 2^0 + 2^3 + 2^4 = 11001 的二进制。
  • 我还有一个疑问。这是否支持对于特定集合有两个具有不同 offerNumber 和 Discount 的条目的情况?
  • 当然,for each o in offer 循环会遍历您提供的所有内容。
  • 我对此真的很陌生。你能告诉我我将如何做到这一点:s &amp; o.setOfItems == 0s + o.setOfItems 其中s 是一个从 0 到 2^k -1 的数字,o.setOfItems 也是一个来自 0 to 2^k -1 的数字。很抱歉打扰你!!
猜你喜欢
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-26
相关资源
最近更新 更多