Find the least number of coins required that can make any change from 1 to 99 cents

后端 未结 27 2006
生来不讨喜
生来不讨喜 2020-12-07 10:08

Recently I challenged my co-worker to write an algorithm to solve this problem:

Find the least number of coins required that can make any change from

相关标签:
27条回答
  • 2020-12-07 10:14

    Here's a simple version in Python.

    #!/usr/bin/env python
    
    required = []
    coins = [25, 10, 5, 1]
    
    t = []
    for i in range(1, 100):
        while sum(t) != i:
            for c in coins:
                if sum(t) + c <= i:
                    t.append(c)
                    break
        for c in coins:
            while t.count(c) > required.count(c):
                required.append(c)
        del t[:]
    
    print required
    

    When run, it prints the following to stdout.

    [1, 1, 1, 1, 5, 10, 10, 25, 25, 25]
    

    The code is pretty self-explanatory (thanks Python!), but basically the algorithm is to add the largest coin available that doesn't put you over the current total you're shooting for into your temporary list of coins (t in this case). Once you find the most efficient set of coins for a particular total, make sure there are at least that many of each coin in the required list. Do that for every total from 1 to 99 cents, and you're done.

    0 讨论(0)
  • 2020-12-07 10:15

    In general, if you have your coins COIN[] and your "change range" 1..MAX, the following should find the maximum number of coins.

    Initialise array CHANGEVAL[MAX] to -1
    
    For each element coin in COIN:
      set CHANGEVAL[coin] to 1
    Until there are no more -1 in CHANGEVAL:
      For each index I over CHANGEVAL:
        if CHANGEVAL[I] != -1:
          let coincount = CHANGEVAL[I]
          For each element coin in COIN:
            let sum = coin + I
            if (COINS[sum]=-1) OR ((coincount+1)<COINS[sum]):
              COINS[sum]=coincount+1
    

    I don't know if the check for coin-minimality in the inner conditional is, strictly speaking, necessary. I would think that the minimal chain of coin-additions would end up being correct, but better safe than sorry.

    0 讨论(0)
  • 2020-12-07 10:15

    Here goes my solution, again in Python and using dynamic programming. First I generate the minimum sequence of coins required for making change for each amount in the range 1..99, and from that result I find the maximum number of coins needed from each denomination:

    def min_any_change():
        V, C = [1, 5, 10, 25], 99
        mxP, mxN, mxD, mxQ = 0, 0, 0, 0
        solution = min_change_table(V, C)
        for i in xrange(1, C+1):
            cP, cN, cD, cQ = 0, 0, 0, 0
            while i:
                coin = V[solution[i]]
                if coin == 1:
                    cP += 1
                elif coin == 5:
                    cN += 1
                elif coin == 10:
                    cD += 1
                else:
                    cQ += 1
                i -= coin
            if cP > mxP:
                mxP = cP
            if cN > mxN:
                mxN = cN
            if cD > mxD:
                mxD = cD
            if cQ > mxQ:
                mxQ = cQ
        return {'pennies':mxP, 'nickels':mxN, 'dimes':mxD, 'quarters':mxQ}
    
    def min_change_table(V, C):
        m, n, minIdx = C+1, len(V), 0
        table, solution = [0] * m, [0] * m
        for i in xrange(1, m):
            minNum = float('inf')
            for j in xrange(n):
                if V[j] <= i and 1 + table[i - V[j]] < minNum:
                    minNum = 1 + table[i - V[j]]
                    minIdx = j
            table[i] = minNum
            solution[i] = minIdx
        return solution
    

    Executing min_any_change() yields the answer we were looking for: {'pennies': 4, 'nickels': 1, 'dimes': 2, 'quarters': 3}. As a test, we can try removing a coin of any denomination and checking if it is still possible to make change for any amount in the range 1..99:

    from itertools import combinations
    
    def test(lst):
        sums = all_sums(lst)
        return all(i in sums for i in xrange(1, 100))
    
    def all_sums(lst):
        combs = []
        for i in xrange(len(lst)+1):
            combs += combinations(lst, i)
        return set(sum(s) for s in combs)
    

    If we test the result obtained above, we get a True:

    test([1, 1, 1, 1, 5, 10, 10, 25, 25, 25])
    

    But if we remove a single coin, no matter what denomination, we'll get a False:

    test([1, 1, 1, 5, 10, 10, 25, 25, 25])
    
    0 讨论(0)
  • 2020-12-07 10:17

    Solution with greedy approach in java is as below :

    public class CoinChange {
        public static void main(String args[]) {
            int denominations[] = {1, 5, 10, 25};
            System.out.println("Total required coins are " + greeadApproach(53, denominations));
        }
    
        public static int greeadApproach(int amount, int denominations[]) {
            int cnt[] = new int[denominations.length];
            for (int i = denominations.length-1; amount > 0 && i >= 0; i--) {
                cnt[i] = (amount/denominations[i]);
                amount -= cnt[i] * denominations[i];            
            }
            int noOfCoins = 0;
            for (int cntVal : cnt) {
                noOfCoins+= cntVal;
            }
            return noOfCoins;
        }
    }
    

    But this works for single amount. If you want to run it for range, than we have to call it for each amount of range.

    0 讨论(0)
  • 2020-12-07 10:19

    Edit: As the commenters have noted, I have misinterpreted the question. (The question is very similar to a basic CS problem I see students at the college having to solve...) waves hand This is not the answer you are looking for. That said, while the original answer is wrong, we can use it as a stepping stone to an O(n) solution.

    So, take the wrong answer below, which only solves for a single value (ie, the minimum coinage required for 68 cents) and simply run it for every value.

    changes = []
    for amount in xrange(1, 100): # [1, 99]
        changes.append( run_the_algo_below( amount ) )
    # Take the maximum for each coin type.
    # ie, if run_the_algo_below returns (q, d, n, p):
    change = [0, 0, 0, 0]
    for c in changes:
        change = [max(c[i], change[i] for i in xrange(0, 4)]
    

    Now, this will certainly give you a valid answer, but is it a minimal answer? (this is the harder part. Currently my gut says yes, but I'm still thinking about this one...)


    (The wrong answer)

    Wow. Loops? Dynamic programming? Really folks?

    In Python:

    amount = ( your_amount_in_cents )
    
    quarters = amount // 25
    dimes = amount % 25 // 10
    nickels = amount % 25 % 10 // 5
    pennies = amount % 25 % 10 % 5
    

    Probably some of those modulo operations can be simplified...

    This isn't hard, you just need to think about how you make change in real life. You give out quarters until adding another quarter would put you over the desired amount, you give out dimes until adding another dime would put you over the desired amount, so on. Then, convert to mathematical operations: modulo and division. Same solution applies for dollars, converting seconds into HH:MM:SS, etc.

    0 讨论(0)
  • 2020-12-07 10:20

    A vb version

    Public Class Form1
    
        Private Sub Button1_Click(ByVal sender As System.Object, _
                                  ByVal e As System.EventArgs) Handles Button1.Click
            For saleAMT As Decimal = 0.01D To 0.99D Step 0.01D
                Dim foo As New CashDrawer(0, 0, 0)
                Dim chg As List(Of Money) = foo.MakeChange(saleAMT, 1D)
                Dim t As Decimal = 1 - saleAMT
                Debug.WriteLine(t.ToString("C2"))
                For Each c As Money In chg
                    Debug.WriteLine(String.Format("{0} of {1}", c.Count.ToString("N0"), c.moneyValue.ToString("C2")))
                Next
            Next
        End Sub
    
        Class CashDrawer
    
            Private _drawer As List(Of Money)
    
            Public Sub New(Optional ByVal QTYtwoD As Integer = -1, _
                           Optional ByVal QTYoneD As Integer = -1, _
                           Optional ByVal QTYfifty As Integer = -1, _
                           Optional ByVal QTYquarters As Integer = -1, _
                           Optional ByVal QTYdimes As Integer = -1, _
                           Optional ByVal QTYnickels As Integer = -1, _
                           Optional ByVal QTYpennies As Integer = -1)
                _drawer = New List(Of Money)
                _drawer.Add(New Money(2D, QTYtwoD))
                _drawer.Add(New Money(1D, QTYoneD))
                _drawer.Add(New Money(0.5D, QTYfifty))
                _drawer.Add(New Money(0.25D, QTYquarters))
                _drawer.Add(New Money(0.1D, QTYdimes))
                _drawer.Add(New Money(0.05D, QTYnickels))
                _drawer.Add(New Money(0.01D, QTYpennies))
            End Sub
    
            Public Function MakeChange(ByVal SaleAmt As Decimal, _
                                       ByVal amountTendered As Decimal) As List(Of Money)
                Dim change As Decimal = amountTendered - SaleAmt
                Dim rv As New List(Of Money)
                For Each c As Money In Me._drawer
                    change -= (c.NumberOf(change) * c.moneyValue)
                    If c.Count > 0 Then
                        rv.Add(c)
                    End If
                Next
                If change <> 0D Then Throw New ArithmeticException
                Return rv
            End Function
        End Class
    
        Class Money
            '-1 equals unlimited qty
            Private _qty As Decimal 'quantity in drawer
            Private _value As Decimal 'value money
            Private _count As Decimal = 0D
    
            Public Sub New(ByVal theValue As Decimal, _
                           ByVal theQTY As Decimal)
                Me._value = theValue
                Me._qty = theQTY
            End Sub
    
            ReadOnly Property moneyValue As Decimal
                Get
                    Return Me._value
                End Get
            End Property
    
            Public Function NumberOf(ByVal theAmount As Decimal) As Decimal
                If (Me._qty > 0 OrElse Me._qty = -1) AndAlso Me._value <= theAmount Then
                    Dim ct As Decimal = Math.Floor(theAmount / Me._value)
                    If Me._qty <> -1D Then 'qty?
                        'limited qty
                        If ct > Me._qty Then 'enough 
                            'no
                            Me._count = Me._qty
                            Me._qty = 0D
                        Else
                            'yes
                            Me._count = ct
                            Me._qty -= ct
                        End If
                    Else
                        'unlimited qty
                        Me._count = ct
                    End If
                End If
                Return Me._count
            End Function
    
            ReadOnly Property Count As Decimal
                Get
                    Return Me._count
                End Get
            End Property
        End Class
    End Class
    
    0 讨论(0)
提交回复
热议问题