Minimum tip to be paid for bill amount B with two kind of coins (x,y) only

旧巷老猫 提交于 2019-12-04 19:21:37
Paul Hankin

You don't need DP to solve this.

First, note that you may as well assume the coins are coprime. Because if they're not then you can only generate multiples of the gcd. Then let g = gcd(x, y) and solve the problem of minimizing the tip T of ceil(B / g) using coins x/g and y/g. Then the solution to the original problem is T*g + g * ceil(B / g) - B.

If x and y are coprime, then the largest number you can't generate exactly is xy - x - y. (See: https://math.stackexchange.com/questions/66963/largest-integer-that-cant-be-represented-as-a-non-negative-linear-combination-o)

So if B > xy - x - y, then you're guaranteed to be able to pay exactly with 0 tip.

Otherwise, you can find the solution using brute force by trying every possible combination of coin x (and then using the smallest number of y to make at least B). Since B < xy, that's approximately y different values. By swapping the coins if necessary, that means we can solve the problem in the worst case in O(min(x, y)) time.

Putting that together into a single program:

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)

You can identify the solution by doing modulus operation with the maximum value and the modulus operation with small value on remainder from the first operation. You can loop over till you identify the maximum deviation. A working code can be found at the following 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}
           ];
      }
  }

output function is at the last but one line. Use this to test with different values.

Take some coin change problem solution with DP (arbitrary example)

Make array length B + 1 + min(x, y)

Fill this array (here you don't need counts, only 0/1 possibility to make a sum)

Look for smallest entry with 1 value in index range [B..B + min(x, y)]

A Mixed-Integer Programming approach will be probably much faster (compared to DP).

Here is an example implementation (built with python and 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)

Call:

solve(x_y_vals, 300)

Output:

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

EDIT: As pointed out by Paul Hankin (thanks!), there was a modelling error. I fixed it.

Remark: The default MIP-solver in CVXPY is only working on toy-problems, so we need a better solver (or we run into numerical troubles). Here we use cbc which is supported by cvxpy, but needs to be installed manually (see the docs).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!