确定分发这些优惠券的最佳方式的算法是什么?

时间:2009-01-08 21:57:32

标签: algorithm

这是我的问题。想象一下,我正在购买3种不同的商品,而且我有多达5张优惠券。优惠券是可以互换的,但在用于不同物品时价值不同。

这是一个矩阵,它给出了在不同项目上花费不同数量的优惠券的结果:

coupons:    1         2         3         4         5
item 1      $10 off   $15 off
item 2                $5 off    $15 off   $25 off   $35 off
item 3      $2 off

我已手动制定了此示例的最佳操作:

  • 如果我有1张优惠券,则第1件可获得10美元优惠券
  • 如果我有2张优惠券,则第1件可以获得15美元优惠券
  • 如果我有3张优惠券,则第1项获得2,第3项获得1,仅需17美元
  • 如果我有4张优惠券,那么
    • 项目1获得1,项目2获得3,总共25美元,或
    • 第2项全部获得4美元,仅售25美元。
  • 如果我有5张优惠券,那么第2项将全部获得5张优惠券。

但是,我需要开发一种通用算法来处理不同的矩阵和任意数量的项目和优惠券。

我怀疑我需要遍历每个可能的组合,以找到 n 优惠券的最佳价格。这里有没有人有任何想法?

6 个答案:

答案 0 :(得分:4)

这是Knapsack问题,或者更确切地说是它的变体。对与此问题相关的算法进行一些研究将指出您的最佳方向。

答案 1 :(得分:4)

这似乎是动态编程的理想选择:

//int[,] discountTable = new int[NumItems][NumCoupons+1]

// bestDiscount[i][c] means the best discount if you can spend c coupons on items 0..i
int[,] bestDiscount = new int[NumItems][NumCoupons+1];

// the best discount for a set of one item is just use the all of the coupons on it
for (int c=1; c<=MaxNumCoupons; c++)
    bestDiscount[0, c] = discountTable[0, c];

// the best discount for [i, c] is spending x coupons on items 0..i-1, and c-x coupons on item i
for (int i=1; i<NumItems; i++)
    for (int c=1; c<=NumCoupons; c++)
        for (int x=0; x<c; x++)
            bestDiscount[i, c] = Math.Max(bestDiscount[i, c], bestDiscount[i-1, x] + discountTable[i, c-x]);

在此结束时,最佳折扣将是bestDiscount [NumItems] [x]的最高值。要重建树,请向后按图:

编辑添加算法:

//int couponsLeft;

for (int i=NumItems-1; i>=0; i++)
{
    int bestSpend = 0;
    for (int c=1; c<=couponsLeft; c++)
        if (bestDiscount[i, couponsLeft - c] > bestDiscount[i, couponsLeft - bestSpend])
            bestSpend = c;

    if (i == NumItems - 1)
        Console.WriteLine("Had {0} coupons left over", bestSpend);
    else
        Console.WriteLine("Spent {0} coupons on item {1}", bestSpend, i+1);

    couponsLeft -= bestSpend;
}
Console.WriteLine("Spent {0} coupons on item 0", couponsLeft);

将图表存储在数据结构中也是一种好方法,但这就是我想到的方式。

答案 2 :(得分:3)

我认为dynamic programming应该这样做。基本上,你跟踪一个数组A [n,c],其数值意味着最优惠的折扣,同时购买n个第一项消费c优惠券。对于n的所有值,[n,0]的值应为0,因此这是一个好的开始。此外,对于所有c,A [0,c]为0。

当您评估A [n,c]时,您循环覆盖项目n的所有折扣优惠,并将该特定优惠的折扣添加到A [n-1,cp],其中p是此特定优惠券的价格折扣。当然必须在此之前计算[n-1,c-p](以相同的方式)。保持最佳组合并存储在阵列中。

递归实现可能会提供最干净的实现。在这种情况下,您应该在A [N,C]中找到答案,其中N是项目总数,C是可用优惠券的总数。

答案 3 :(得分:2)

这可以写成linear programming问题。对于大多数“典型”问题,单纯形法是解决此类问题的快速,相对简单的方法,或者有开源LP求解器可用。

对于你的例子:

0&lt; = xi&lt; = 1

  • x1 =只有一张优惠券用于第1项,否则为零
  • x2 =只有两张优惠券用于第1项,否则为零
  • x3 =只有一张优惠券用于第2项,否则为零
  • x4 =只有两张优惠券用于第2项,否则为零
  • x5 =只有三张优惠券用于第3项,否则为零
  • ...

假设如果我在第1项上花了两张优惠券,那么x1和x2都是一张。这意味着约束

  • x1&gt; = x2

对其他项目有类似的约束,例如,

  • x3&gt; = x4
  • x4&gt; = x5

节省的金额是

  • 已保存= 10 x1 + 5 x2 + 0 x3 + 5 x4 + 10 x5 + ...

如果您想找到使用固定数量优惠券保存的最多钱,那么您希望根据上述约束和附加约束最小化已保存

  • 优惠券数量= x1 + x2 + x3 + ...

这适用于任何矩阵和项目数。改变符号(并且感到难过,我无法做下标),如果 j 优惠券花在项目编号上,则 0&lt; = y_ij&lt; = 1 为一个< EM> I 的。我们有约束

  • y_i(j-1)&gt; = y_ij

如果在项目 i 上花费 j 优惠券节省的金额为 M_ij ,我们在其中定义 M_i0 = 0 ,然后最大化

  • 已保存= Sum_ij(M_ij - M_i(j-1))y_ij

受上述限制和

的约束
  • 优惠券数量= Sum_ij y_ij

(斜体格式似乎不适用于此处)

答案 4 :(得分:0)

我怀疑用于记忆每个优惠券计数的某种排序列表可以在这里提供帮助。

例如,如果你有4张优惠券,最佳可能是:

  1. 在所有东西上使用全部4。你必须检查所有这些新价格。
  2. 使用3和1. 3项是3个优惠券的最佳解决方案,或者该项目与1个优惠券项目的三个首选项重叠,在这种情况下,您需要找到其中一个优惠券的最佳组合三张最佳1张优惠券和3张优惠券。
  3. 使用2和2.找到前3个2项。如果#1和#2重叠,#1和#3,除非它们也重叠,在这种情况下#2和#3不重叠。
  4. 这个答案很模糊。我需要多考虑一下。

答案 5 :(得分:-2)

此问题在概念上类似于Traveling Salesman problem,其中O(n!)最适合查找最佳解决方案。有几个快捷方式可以采取,但他们需要大量的时间来发现,我怀疑你有。

假设我们正在处理少量优惠券,那么检查每种可能的组合将最有效地利用您的时间。让客户等一下,而不是花多年时间。