Choosing coins with least or no change given

前端 未结 8 1400
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-07 04:00

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 coin

相关标签:
8条回答
  • 2021-02-07 04:39

    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;
    }
    

    Here is my test code:

    $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";
    

    The result:

    [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!

    0 讨论(0)
  • 2021-02-07 04:45

    I have come up with the following solution. If others can critique it for me I would appreciate it.

    <?php
    
    $coin_value = array(10,5,3,1);
    $inventory = array(1,2,0,2);
    $price = 17;
    
    for ($i = 3; $i >= 0; $i--){
    
            $btotal = 0;
            $barray = array();
    
            for ($j = 0; $j < 4; $j++){
                    $remaining = $price - $btotal;
                    $to_add = floor($remaining / $coin_value[$j]);
    
                    if ($i != 3 && $i == $j){
                            $to_add++;
                    }
    
                    if ($inventory[$j] < $to_add){
                            $to_add = $inventory[$j];
                    }
    
                    $btotal += $to_add * $coin_value[$j];
    
                    for ($k = 0; $k < $to_add; $k++){
                            $barray[] = $coin_value[$j];
                    }
    
                    if ($btotal >= $price)
                            break 2; //warning: breaks out of outer loop
    
            }
    }
    
    $change_due = $btotal - $price;
    
    print_r($barray);
    
    echo "Change due: \$$change_due\n";
    
    ?>
    

    It covers examples 1 and 2 in my original question, but does not cover example 3. However, I think it will do for now unless someone can come up with a better solution. I decided not to use recursion as it would seem to take too much time.

    0 讨论(0)
提交回复
热议问题