仅使用两种硬币(x,y)支付账单金额B的最小提示

时间:2016-05-13 19:26:42

标签: algorithm math data-structures dynamic-programming discrete-mathematics

我有两种硬币(每种类型的无限硬币)。 这两个硬币的值是 x和y 。 我必须支付一笔费用 B

  

我需要支付的最低金额为提示

tip can be any value >=0

目标是尽量减少小费。

我正在考虑动态编程方法。或者任何更快的方法。 请帮忙。

function minTip(x,y,B){
    if(z<=0) return -z;
    return minimum( minTip(x,y,B-x),minTip(x,y,B-y) );
}
  

任何人都可以帮助DP方法。??

4 个答案:

答案 0 :(得分:2)

你不需要DP来解决这个问题。

首先,请注意您可以假设硬币是互质的。因为如果他们不是那么你只能生成gcd的倍数。然后让g = gcd(x,y)并使用硬币x / g和y / g解决使细胞尖端T(B / g)最小化的问题。然后原始问题的解是T * g + g * ceil(B / g) - B。

如果x和y 互质,那么你无法生成的最大数字是xy - x - y。 (见:https://math.stackexchange.com/questions/66963/largest-integer-that-cant-be-represented-as-a-non-negative-linear-combination-o

所以,如果B> xy - x - y,然后你保证能够用0提示付款。

否则,您可以通过尝试硬币x的每种可能组合(然后使用最小数量的y来制作至少B)来使用强力来找到解决方案。由于B < xy,大约是y个不同的值。通过在必要时交换硬币,这意味着我们可以在最坏的情况下在O(min(x,y))时间内解决问题。

将它们组合成一个程序:

def gcd(x, y):
    x, y = min(x, y), max(x, y)
    while x != 0:
        x, y = y % x, x
    return y

def tip(x, y, B):
    g = gcd(x, y)
    if g != 1:
        nB = (B + g - 1) // g
        T = tip(x // g, y // g, (B + g - 1) // g)
        return T * g + nB * g - B
    if B > x * y - x - y:
        # We're guaranteed to be able to make B exactly.
        return 0
    # Swap the coins if necessary so that x is the larger one.
    x, y = max(x, y), min(x, y)
    T = B
    # Try 0, 1, 2, ... B//x+1 of the x coin.
    # More than this isn't necessary since (B//x+1)*x
    # is already greater than or equal to B.
    for i in xrange(B // x + 2):
        # j is the smallest number of y coins
        # such that ix + jy >= B.
        j = max(0, (B - i * x + y - 1) // y)
        T = min(T, i * x + j * y - B)
    return T

print tip(7, 12, 20)

答案 1 :(得分:1)

您可以通过使用最大值进行模运算来识别解决方案,并使用小值对第一个运算的余数进行模数运算。您可以循环直到找到最大偏差。 可以在以下bin中找到工作代码。

http://jsbin.com/fivikoh/edit?js,console

tipReduce(x,y,z) {
     //find minimum and maximum from x and y
     //find the quotient by dividing z with maxValue
     for(//decrease the quotient till 0) {
      //multiply the quotient with max and the divide the reminder
      //with min value, if the reminder is the zero 'return' it, it is the
      //answer. else store the reminder to find the maximum value of the
      //reminder later. That is used for the tip

        var result =  k * maxValue;
       var reminder =  z - result;
       var reminderDivision = reminder % minValue;
       var divisionWithMinValue = Math.floor(reminder/minValue);
       var calcDifference = z - (result + (divisionWithMinValue *     minValue));
    if(calcDifference > smallestPossible) {
     smallestPossible = calcDifference;
     rememberValues = [k, divisionWithMinValue];

    }
    if(reminderDivision === 0) {
      return [{denom: maxValue, count:k},
            {denom:minValue, count: (reminder/minValue)},
            {tip:0}
           ];
      }
  }

输出函数位于最后一行但是一行。使用它来测试不同的值。

答案 2 :(得分:0)

Mixed-Integer Programming方法可能快得多(与DP相比)。

这是一个示例实现(使用python和cvxpy构建):

from cvxpy import *

x_y_vals = [3,7]

def solve(vals, amount):
    vars = Int(2)
    constraints = [sum_entries(mul_elemwise(vals, vars)) >= amount,
                   vars >= 0]
    objective = Minimize(sum_entries(mul_elemwise(vals, vars)))

    prob = Problem(objective, constraints)
    prob.solve(solver=CBC)
    print("status:", prob.status)
    print("optimal diff", prob.value)
    print("optimal var", vars.value)

solve(x_y_vals, 300)

呼叫:

solve(x_y_vals, 300)

输出:

('status:', 'optimal')
('optimal diff', 300.0)
('optimal var', matrix([[  2.],
        [ 42.]]))

编辑:正如 Paul Hankin 所指出的那样(谢谢!),出现了建模错误。我修好了。

备注: CVXPY中的默认MIP解算器仅适用于玩具问题,因此我们需要更好的求解器(或者遇到数字问题)。在这里,我们使用 cvxpy 支持的cbc,但需要手动安装(请参阅文档)。

答案 3 :(得分:0)

使用DP(arbitrary example

获取一些硬币更换问题解决方案

制作数组长度B + 1 + min(x, y)

填写此数组(此处您不需要计数,只有0/1可以进行总和)

在索引范围[B..B + min(x,y)]

中寻找具有1个值的最小条目