我必须在我的应用程序中解决这个问题:卖家有一个他在HashMap中所有monney的钱包(价值和数量)。
例如:
用户向卖家付款,如果计数不好,卖家必须取钱。我必须制作一个算法,给出最少的硬币以获得完全相同的值(在散列图中)。
例如我必须回馈 26欧元所以我必须提供2张10欧元和3欧元2欧元的门票,并且返回的hashmap必须是这样的:
这是问题所在,因为如果算法采用第一个元素而不知道是否可以拥有正确的帐户,那么它将提供2张10欧元的票,然后在1张5票之后以及之后被阻止因为给予2欧元更多给你总和27。
如何获得良好的算法呢?
我试试这个:
for ((value, quantity) in my_wallet_map){
var q = quantity
while (q > 0){
if (!isPossibleToPayFromBelow(value, amount, my_wallet_map)){
what_to_pay_map[value] = (what_to_pay_map[value] ?: 0.toLong()) + 1.toLong()
my_wallet_map[value] = (my_wallet_map[value] ?: 0.toLong()) - 1.toLong()
amount -= value
}
if (calculTotalFromHashMapInLong(what_to_pay_map) == amount_no_modified)
break
q -= 1.toLong()
}
if (calculTotalFromHashMapInLong(what_to_pay_map) == amount_no_modified)
break
}
使用功能:
fun calculTotalFromHashMapInLong(map: HashMap<Long, Long>): Long{
var total: Long = 0
for ((value, quantity) in map)
total += value * quantity
return total
}
fun isPossibleToPayFromBelow(v: Long, t: Long, map: SortedMap<Long, Long>): Boolean{
val new_map = map.toSortedMap(reverseOrder())
var total: Long = 0.toLong()
for ((value, quantity) in new_map){
if (quantity != 0.toLong() && value < v) {
total += (value * quantity)
}
}
return total > t
}
但结果并不好。我尝试了很多东西。
答案 0 :(得分:3)
据我所知,对此没有完美而快速的解决方案。 但是,你可以先尝试一个贪婪的算法,如果它失败了,你可以尝试一个精确的算法(不幸的是,这可能非常慢)。
对不起,但我不太了解Kotlin,但是因为你只想要一个算法,我用Python编写了pyseonics最少的东西,所以你可以很容易地把它转换成科特林。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Give back money algorithm."""
DEBUG = True
def print_debug(msg):
"""Prints if DEBUG is set to True."""
if DEBUG:
print(" --> {}".format(msg))
# Works great with a structure
# cash_list = [[value_of_coin_1, number_of_coins_1],
# [value_of_coin_2, number_of_coins_2],
# ...]
# sorted desc. on value
def greedy(cash_list, value):
"""Tries to gather the requested sum by taking each time the largest
"coins" avaible."""
give_cash_list = []
for (v, n) in cash_list:
n_of_v = min(n, value // v)
give_cash_list.append([v, n_of_v])
value -= v * n_of_v
if value == 0:
return give_cash_list
return None
def exact(cash_list, value):
"""Examines all the possible ways to gather the requested sum."""
pre_solution_pool = [[[], 0]]
final_solution_pool = []
for (v, n) in cash_list:
new_pre_solution_pool = []
for (p_s, p_v) in pre_solution_pool:
new_pre_solution_pool.append([p_s, p_v]) # 0 of the new coin
for j in range(1, min(n, (value - p_v) // v) + 1):
new_pre_solution = [p_s + [[v, j]], p_v + v * j]
if new_pre_solution[1] == value:
final_solution_pool.append(new_pre_solution[0])
else:
new_pre_solution_pool.append(new_pre_solution)
pre_solution_pool = new_pre_solution_pool
return final_solution_pool
def nb_of_coins(solution):
"""Returns the number of coins of a solution."""
return sum([n for v, n in solution])
def best_exact(final_solution_pool):
"""Returns the best solution from the exact algorithm."""
if final_solution_pool:
return sorted(final_solution_pool, key=nb_of_coins)[0]
else:
return None
def compute_new_cash_list(cash_list, give_cash_list):
"""Returns the new cash_list where coins of give_cash_list have been
removed."""
new_cash_list = []
(m, n) = (len(cash_list), len(give_cash_list))
(i, j) = (0, 0)
while i < m and j < n:
(a, b) = cash_list[i]
(c, d) = give_cash_list[j]
if a == c:
new_cash_list.append([a, b - d])
i += 1
j += 1
else:
new_cash_list.append([a, b])
i += 1
return new_cash_list
def give_back(cash_list, value):
"""Best of the two worlds."""
g = greedy(cash_list, value)
if g is not None:
return g, compute_new_cash_list(cash_list, g)
print_debug("Greedy failed for {} : {}".format(cash_list, value))
e = best_exact(exact(cash_list, value))
if e is not None:
return e, compute_new_cash_list(cash_list, e)
print_debug("Exact failed for {} : {}".format(cash_list, value))
return None, cash_list
def main():
"""Launcher."""
my_cash_list = [[5000, 18], [1000, 2], [500, 1], [200, 5], [10, 3]]
my_value = 2600
print(give_back(my_cash_list, my_value))
if __name__ == "__main__":
main()
我希望它有所帮助。随意提出任何问题!
答案 1 :(得分:1)
想象一下,你有以下笔记
|-----|------|
|count|value |
|-----|------|
| 1|20.00 |
| 2|10.00 |
| 3| 5.00 |
| 3| 2.00 |
|-----|------|
你想得到21.00然后贪婪算法需要20并且卡住了。在这种情况下,您必须尝试所有组合。
蛮力方法可以起作用,假设小袋中没有太多硬币,否则尝试的组合会很快变大。
// Sorting the map forces an order
val money = mapOf(
5000 to 18,
1000 to 2,
500 to 1,
200 to 5,
10 to 3
).toSortedMap()
// The target amount to find a way of making
val target = 2600
val options = money.asSequence().fold(listOf(listOf<Int>())) { acc, (value, count) ->
// For each denomination add all the possible counts
acc.flatMap { option -> (0..count).map { amount -> option + amount } }
}.map { it.zip(money.keys) }
val result = options
// Only use the counts that give the right change
.filter { it.map { (a, b) -> a * b }.sum() == target }
// Find the least amount of notes
.minBy { it.map { it.first }.sum() }
if (result == null) {
println("Can't make the change")
} else {
println(result)
}
您可以限制选项,以便只创建&lt; = target的那些,我没有这样做以保持代码简短,并且对于您提供的数字,完整的组合集仍然很小。< / p>
答案 2 :(得分:0)
您可以使用简单的动态编程算法来查找生成更改所需的最小硬币数。
val coins=listOf(1,2,5,10)
tailrec fun change(target: Int): Int = when (target){
0 -> 0
else -> coins.filter{ it <= target}.map { change(target-it) + 1 }.min()!!
}