选择零钱或零钱的硬币 [英] Choosing coins with least or no change given

查看:84
本文介绍了选择零钱或零钱的硬币的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个游戏,游戏币面额为$ 10,$ 5,$ 3和$ 1。玩家的库存中每种类型的货币可能有0或更多,总共最多15个硬币。我试图弄清楚如何正确选择硬币,以使找回的零钱最少。起初我以为这很容易解决,但是现在我束手无策了。

I am making a game which consists of coin denominations of $10, $5, $3, and $1. The player may have 0 or more of each type of currency in his inventory with a maximum of 15 coins in total. I am trying to figure out how to properly select coins so that the least amount of change is given in return. At first I thought this was going to be easy to solve, but now I'm having trouble wrapping my head around it.

这里有两个例子可以进一步说明情况:

Here are two examples that explain the situation further:

示例1:

用户携带以下硬币:$ 5, 3美元,3美元,3美元,1美元,1美元,1美元,1美元,并想以12美元的价格购买商品。解决方案是用$ 5,$ 3,$ 3,$ 1支付并且不找零。

The user is carrying these coins: $5, $3, $3, $3, $1, $1, $1, $1 and want to buy an item for $12. The solution would be to pay with $5, $3, $3, $1 and give no change.

示例2:

用户没有任何$ 1硬币,并且携带$ 5,$ 3,$ 3,$ 3,$ 3。一件商品的价格为12美元,因此他们分别支付了5美元,3美元,3美元和3美元,并返还了2美元的零钱。

The user does not have any $1 coins, and is carrying $5, $3, $3, $3, $3. An item is bought for $12 so they pay with $5, $3, $3, and $3 and change of $2 is given back.

由于我们首先选择了较大的硬币,因此我不知道如何知道玩家的库存中是否有足够的价值较低的硬币(在这种情况下为1美元)来容纳示例1,以及是否不足以使用更多的价值较高的硬币(例如本示例) 2。

Since we select the larger coins first, what I can't figure out is how to know if there are enough lower valued coins ($1 in this case) in the player's inventory to accommodate example 1, and if there aren't enough to use more of the higher valued coins as in example 2.

在下面的示例中看到了另一个问题,尽管我很高兴能使上述两个示例正常工作:

A further issue is seen in the following example, though I'd be happy just getting the above two examples working:

示例3:
用户拿着以下硬币:$ 5,$ 3,$ 3,$ 3。玩家花了6美元买了东西。最好使用$ 3和$ 3而不返回任何变化,而不是使用$ 5和$ 3并给出$ 2的变化。

Example 3: The user is carrying these coins: $5, $3, $3, $3. The player buys something for $6. It would be better to use $3 and $3 and return no change rather than using $5 and $3 and give $2 in change.

我相信可以使用以下方法解决前两个示例递归和贪婪算法的变体。

I believe the first two examples can be solved using recursion and a variation of the greedy algorithm.

对于赏金奖励:

我现在在下面添加了自己的答案作为临时解决方案。但是,我喜欢下面Llama先生的方法(请参阅他引用的链接),并希望找到一个PHP示例来满足此要求。我相信这种方法不需要递归,并且可以使用记忆。

I have added my own answer below as a temporary solution for now. However, I like the approach of Mr. Llama's below (see the link he references) and would like to find a PHP example to satisfy this. I believe this approach does not need recursion and uses memoization.

如果有多个选项可以使更改量最小,那么我希望将二者结合起来

If there are multiple options for the least amount of change then I would like the tie to be given to the one that pays with the least amount of coins.

推荐答案

此答案基于גלעד-ברקן的答案。我根据他的要求将其张贴在这里。虽然没有一个答案是我一直在寻找的答案,但我发现这是发布的最佳选择。这是我当前正在使用的修改算法:

This answer is based off of גלעד-ברקן's answer. I am posting it here as per his request. While none of the answers were quite the one that I was looking for I found that this was the best option posted. Here is the modified algorithm that I am currently using:

<?php

function leastChange($inventory, $price){

    //NOTE: Hard coded these in the function for my purposes, but $coin value can be passed as a parameter for a more general-purpose algorithm
    $num_coin_types = 4;
    $coin_value = [10,5,3,1];

    $have = 0;
    for ($i=0; $i < $num_coin_types; $i++){
            $have += $inventory[$i] * $coin_value[$i];
    }

    //NOTE: Check to see if you have enough money to make this purchase
    if ($price > $have){
            $error = ["error", "Insufficient Funds"];
            return $error;
    }

    $stack = [[0,$price,$have,[]]];
    $best = [-max($coin_value),[]];

    while (!empty($stack)){

            // each stack call traverses a different set of parameters
            $parameters = array_pop($stack);

            $i = $parameters[0];
            $owed = $parameters[1];
            $have = $parameters[2];
            $result = $parameters[3];

            if ($owed <= 0){
                    //NOTE: check for new option with least change OR if same as previous option check which uses the least coins paid
                    if ($owed > $best[0] || ($owed == $best[0] && (array_sum($result) < array_sum($best[1])))){

                            //NOTE: add extra zeros to end if needed
                            while (count($result) < 4){
                                    $result[] = 0;
                            }
                            $best = [$owed,$result];
                    }
                    continue;
            }

            // skip if we have none of this coin
            if ($inventory[$i] == 0){
                    $result[] = 0;
                    $stack[] = [$i + 1,$owed,$have,$result];
                    continue;
            }

            // minimum needed of this coin
            $need = $owed - $have + $inventory[$i] * $coin_value[$i];

            if ($need < 0){
                    $min = 0;
            } else {
                    $min = ceil($need / $coin_value[$i]);
            }

            // add to stack
            for ($j=$min; $j<=$inventory[$i]; $j++){
                    $stack[] = [$i + 1,$owed - $j * $coin_value[$i],$have - $inventory[$i] * $coin_value[$i],array_merge($result,[$j])];
                    if ($owed - $j * $coin_value[$i] < 0){
                            break;
                    }
            }
    }

    return $best;
}

这是我的测试代码:

$start = microtime(true);

$inventory = [0,1,3,4];
$price = 12;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";



$inventory = [0,1,4,0];
$price = 12;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";

$inventory = [0,1,4,0];
$price = 6;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";


$inventory = [0,1,4,0];
$price = 7;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";


$inventory = [1,3,3,10];
$price=39;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";

$inventory = [1,3,3,10];
$price=45;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";

//stress test
$inventory = [25,25,25,1];
$price=449;
echo "\n";
echo json_encode(leastChange($inventory,$price));
echo "\n";

$time_elapsed = microtime(true) - $start;
echo "\n Time taken: $time_elapsed \n";

结果:

[0,[0,1,2,1]]

[0,[0,0,4,0]]

[0,[0,0,2,0]]

[-1,[0,1,1,0]]

[0,[1,3,3,5]]

["error","Insufficient Funds"]

[-1,[25,25,25,0]]

Time taken: 0.0046839714050293

当然,时间以微秒为单位,因此执行时间为一秒钟!

Of course that time is in microseconds and therefore it executed in a fraction of a second!

这篇关于选择零钱或零钱的硬币的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆