we have 2 kinds of stamp with values $A and $B, The
number of stamps is infinite, we have a letter to send out and need $T
postage, question is to find out the lowest cost required to get $T postage? Pls describe your method
and code it with any language you are good at( even we perfer C/C++ :)。
example as below,
Input:A B T
1 ≤ A < B ≤ 10e9
1 ≤ T ≤ 10e9
A, B, T are integers
Output: The lowest value of 2 kinds of stamps combination which should
be >= $T
As a human being we first came to a brute-force greedy algorithm w/o thinking, which was:
def greedy(a, b, t):
num_of_b = t // b
num_of_a = 1 if t % b else 0
cost = num_of_a * a + num_of_b * b
return {'cost': cost, 'num_of_a': num_of_a, 'num_of_b': num_of_b}Then after a cup of tea we realize that this cannot cover all the cases.
For example, let's say a = 3 and b = 10 and finally t = 12.
If we using the solution above we have 10 + 3 == 13 as a result.
But obviously 3 + 3 + 3 + 3 == 12 is a more acceptable answer.
So we are thinking if there is a way to do it more elegantly?
After a long-time searching the Internet. Finally I got this solution, which I think it emulates the real world scenario.
Let
tbe your target andaandb < abe the values of the stamps you have. Letn = ⌈t/a⌉be the maximum number ofastamps it makes sense to use.For each number
ifromnto0, consider the cost of the solution with:
istamps of valueaandj = ⌈(t - i*a) / b⌉stamps of valuebThe cost of each solution would obviously be
i*a + j*b.Your solution is the pair
(i, ⌈(t - i*a) / b⌉ )that minimizes said cost. (Note there's only one unknown in that pair,i.)
Note: This answer is assuming a > b but the raw problem is assuming a < b. And it doesn't matter~
And we do a quick coding work:
import math
def yet_another_solution(a, b, t):
n = math.ceil(t / b)
cost = []
for i in range(n + 1):
j = math.ceil((t - i * b) / a)
cost.append({'cost': i * b + j * a, 'num_of_a': j, 'num_of_b': i})
cost.sort(key=lambda x: x.get('cost'))
return cost[0]Voila! We have achieved the goal of this problem.
And the original answer noticed that if we find the division without rest. Then we can give up searching and stop immediately.
An additional optimization would be to stop immediately as soon as a division without rest is performed, either when determining
norj. A division without rest means that we have found a solution that perfectly pays the due amount and cannot thus be improved on. (There may be others like it, but none that is actually better, so there's no need to keep searching).
And there is also a "dp" solution(Why using quotation mark? bc I'm not familiar with those dp stuff... and I also don't know why it works /shrug).
# credit to my kyodai(@tingyume)
def dp_solution(a, b, t):
def dp(x):
if not x:
return 0
if x <= a:
return a
if x <= b:
return min(b, dp(x - a) + a)
return min(dp(x - b) + b, dp(x - a) + a)
return dp(t)
Bonus
Thoughts
首先为我自己的逻辑思维, 语言组织和表达能力, 写作能力的严重匮乏而道歉.
我自认为在上面的回应中很清晰的表述了针对
A, B比较小, T比较大这个特定的场景的一种情况的优化方案.我没有说要
限制A, B是不是互质, 没有任何这个意思.但是你问, 你一定要觉得要问我, 对
A, B比较小, T比较大这个特定的场景有没有好的办法可以加速.我说有, 我就明确地告诉你这一点. 只是上述方案需要先判断
A, B是不是互质.那么既然都得罪了, 不妨就顺着这个思路继续补充下去, 我们来讨论非互质的情况.
首先我想再把这种情况分为两种子链路, 一条是二者相除可以得到一个整数, 另一条是不能.
A ÷ B ∈ ℤ
这种情况下其实在问题本体中已经讨论过了, 我这里再强调一下其中的一些关键点.
直接拿目标邮资
T与较小价值的邮票A做ceiling division操作得出一个正整数r.然后用
r与A相乘得出一个近似T的数字T', 此数字即为此题的解.有一种特殊情况是当正好整除的时候
T' = T.A ÷ B ∉ ℤ
这种情况可以排除奇数的情况, 因为若不互质, 又不是互相为整数倍数, 是不可能为奇数的.
则此处二者均为偶数, 根据此处的陈述, 可以判断出能获得的结果肯定是二者的最大公约数的倍数
则可先求出此最大公约数, 然后作为除数与
T做ceiling division操作, 之后就跟上面整数的流程是一致的了.一个相对较快的通用的解法
其实这样的方法是不存在的, 就好比 CAP theorem 一样, 有要满足这一点, 就必须牺牲那一点.
这也是为什么前后三篇拙作通篇离不开
空谈与实际.就算是一块 STM32, 又有谁会拿她去计算圆周率的前一百亿位呢 :P
Credit
https://gist.github.com/hldh214/0fd4fc4b7b7a61df1e09ad410863555b#gistcomment-3635420