我有两种硬币(每种类型的无限硬币)。 这两个硬币的值是 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方法。??
答案 0 :(得分:2)
你不需要DP来解决这个问题。
首先,请注意您可以假设硬币是互质的。因为如果他们不是那么你只能生成gcd的倍数。然后让g = gcd(x,y)并使用硬币x / g和y / g解决使细胞尖端T(B / g)最小化的问题。然后原始问题的解是T * g + g * ceil(B / g) - B。
如果x和y 是互质,那么你无法生成
所以,如果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个值的最小条目