如何计算代表n美分的方式数量

时间:2017-03-07 18:17:43

标签: ruby algorithm recursion

我正在研究以下算法,并想知道我的实现是否正确:

  

鉴于无数宿舍,角钱,镍币和便士,   编写代码来计算代表n美分的方式数

这没有记忆:

def count_ways(n)
  return 0 if n < 0 
  return 1 if n == 0 

  count_ways(n-25) + count_ways(n-5) + count_ways(n-10) + count_ways(n-1)
end

3 个答案:

答案 0 :(得分:4)

不,你将是重复计算的解决方案,因为你可以首先选择一个四分之一,然后是一角钱,或者相反,但这些解决方案基本相同。

防止重复计算的最简单方法是确保你从不挑选比你已经选择的硬币大的硬币。

在代码中:

def count_ways(n, max_coin)
  return 0 if n < 0 
  return 1 if n == 0 

  result = count_ways(n-1, 1)
  result = result + count_ways(n- 5,  5) if max_coin >=  5
  result = result + count_ways(n-10, 10) if max_coin >= 10
  result = result + count_ways(n-25, 25) if max_coin >= 25
  result
end

并将其称为25作为初始最大硬币

答案 1 :(得分:2)

我们可以很容易地看到您的代码是否正确。让我们尝试换一毛钱。有四种方式:1角钱,2个镍币,1个镍和5个便士,10个便士,但count_ways(10) #=> 9

您可以使用递归来执行以下操作。

<强>代码

def count_ways(cents, coins)
  if coins.size == 1
    return (cents % coins.first) == 0 ? [cents/coins.first] : nil
  end 
  coin, *remaining_coins = coins
  (0..cents/coin).each_with_object([]) { |n, arr|
    count_ways(cents-n*coin, remaining_coins).each { |a| arr << [n, *a] } }
end 

<强>实施例

coins = [25, 10, 5, 1]

count_ways(32, coins)
  #=> [[0, 0, 0, 32], [0, 0, 1, 27], [0, 0, 2, 22], [0, 0, 3, 17], [0, 0, 4, 12],
  #    [0, 0, 5,  7], [0, 0, 6,  2], [0, 1, 0, 22], [0, 1, 1, 17], [0, 1, 2, 12],
  #    [0, 1, 3,  7], [0, 1, 4,  2], [0, 2, 0, 12], [0, 2, 1,  7], [0, 2, 2,  2],
  #    [0, 3, 0,  2], [1, 0, 0,  7], [1, 0, 1,  2]] 

count_ways(100, coins)
  #=> [[0, 0, 0, 100], [0, 0, 1, 95], [0, 0, 2, 90], [0, 0, 3, 85], [0, 0, 4, 80],
  #    [0, 0, 5,  75], [0, 0, 6, 70], [0, 0, 7, 65], [0, 0, 8, 60], [0, 0, 9, 55],
  #    ...
  #    [3, 1, 2,   5], [3, 1, 3,  0], [3, 2, 0,  5], [3, 2, 1,  0], [4, 0, 0,  0]] 
count_ways(100, coins).size
  #=> 242 

<强>解释

显示递归如何工作的最好方法是使用puts语句对代码进行加密,然后针对一个简单的示例运行它。

INDENT = 8
@indentation = 0

def indent
 @indentation += INDENT
end

def undent
 @indentation = [@indentation-INDENT, 0].max
end

def ind
  ' '*@indentation
end

def count_ways(cents, coins)
  puts "#{ind}** entering count_ways with cents=#{cents}, coins=#{coins}"
  if coins.size == 1
    puts "#{ind}<< returning [cents]=#{[cents]} as coins.size == 1" 
    undent
  end  
  return [cents] if coins.size == 1
  coin, *remaining_coins = coins
  puts "#{ind}coin=#{coin}. remaining_coins=#{remaining_coins}"
  puts "#{ind}0..cents/coin=#{0..cents/coin}"
  arr = (0..cents/coin).each_with_object([]) do |n, arr|
    puts "#{ind}  n=#{n}, arr=#{arr}"
    puts "#{ind}  >> calling count_ways(#{cents}-#{n}*#{coin}, remaining_coins)"
    indent
    aa = count_ways(cents-n*coin, remaining_coins)
    puts "#{ind}  aa=#{aa}"
    aa.each do |a|
      arr << [n, *a]
      puts "#{ind}    arr << [#{n}, *#{a}], arr=#{arr}"
    end
    puts "#{ind}  after all coins, arr=#{arr}"
  end
  puts "#{ind}<< returning arr=#{arr}"
  undent
  arr
end

现在让我们运行count_ways(12, coins),它应该返回12美分的四种变更方式:[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]

count_ways(12, coins)
** entering count_ways with cents=12, coins=[25, 10, 5, 1]
coin=25. remaining_coins=[10, 5, 1]
0..cents/coin=0..0
  n=0, arr=[]
  >> calling count_ways(12-0*25, remaining_coins)
        ** entering count_ways with cents=12, coins=[10, 5, 1]
        coin=10. remaining_coins=[5, 1]
        0..cents/coin=0..1
          n=0, arr=[]
          >> calling count_ways(12-0*10, remaining_coins)
                ** entering count_ways with cents=12, coins=[5, 1]
                coin=5. remaining_coins=[1]
                0..cents/coin=0..2
                  n=0, arr=[]
                  >> calling count_ways(12-0*5, remaining_coins)
                        ** entering count_ways with cents=12, coins=[1]
                        << returning [cents]=[12] as coins.size == 1

                  aa=[12]
                    arr << [0, *12], arr=[[0, 12]]
                  after all coins, arr=[[0, 12]]
                  n=1, arr=[[0, 12]]
                  >> calling count_ways(12-1*5, remaining_coins)
                        ** entering count_ways with cents=7, coins=[1]
                        << returning [cents]=[7] as coins.size == 1
                  aa=[7]
                    arr << [1, *7], arr=[[0, 12], [1, 7]]
                  after all coins, arr=[[0, 12], [1, 7]]
                  n=2, arr=[[0, 12], [1, 7]]
                  >> calling count_ways(12-2*5, remaining_coins)
                        ** entering count_ways with cents=2, coins=[1]
                        << returning [cents]=[2] as coins.size == 1

                  aa=[2]
                    arr << [2, *2], arr=[[0, 12], [1, 7], [2, 2]]
                  after all coins, arr=[[0, 12], [1, 7], [2, 2]]
                << returning arr=[[0, 12], [1, 7], [2, 2]]
          aa=[[0, 12], [1, 7], [2, 2]]
            arr << [0, *[0, 12]], arr=[[0, 0, 12]]
            arr << [0, *[1, 7]], arr=[[0, 0, 12], [0, 1, 7]]
            arr << [0, *[2, 2]], arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
          after all coins, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
          n=1, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2]]
          >> calling count_ways(12-1*10, remaining_coins)
                ** entering count_ways with cents=2, coins=[5, 1]
                coin=5. remaining_coins=[1]
                0..cents/coin=0..0
                  n=0, arr=[]
                  >> calling count_ways(2-0*5, remaining_coins)
                        ** entering count_ways with cents=2, coins=[1]
                        << returning [cents]=[2] as coins.size == 1

                  aa=[2]
                    arr << [0, *2], arr=[[0, 2]]
                  after all coins, arr=[[0, 2]]
                << returning arr=[[0, 2]]
          aa=[[0, 2]]
            arr << [1, *[0, 2]], arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
          after all coins, arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
        << returning arr=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
  aa=[[0, 0, 12], [0, 1, 7], [0, 2, 2], [1, 0, 2]]
    arr << [0, *[0, 0, 12]], arr=[[0, 0, 0, 12]]
    arr << [0, *[0, 1, 7]], arr=[[0, 0, 0, 12], [0, 0, 1, 7]]
    arr << [0, *[0, 2, 2]], arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2]]
    arr << [0, *[1, 0, 2]], arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
  after all coins, arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
<< returning arr=[[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]]
 => [[0, 0, 0, 12], [0, 0, 1, 7], [0, 0, 2, 2], [0, 1, 0, 2]] 

答案 2 :(得分:1)

硬币的顺序并不重要,所以coins.min在这种情况下不会帮助你 - 它使事情过于复杂。

首先,我们必须建立一种关于硬币种类与计数变化量之间关系的直觉

  

使用a种硬币更改金额n的方法数量等于

     
      
  • 使用除第一种硬币之外的所有内容更改金额a的方式的数量,加上
  •   
  • 使用所有a − d种硬币更改金额n的方法数量,其中d是第一种硬币的面额。
  •   
     

来源:SICP Chapter 1.2

### change_coins :: (Int, [Int]) -> Int
def change_coins amount, (x,*xs)
  if amount == 0
    1
  elsif amount < 0 or x.nil?
    0
  else
    change_coins(amount, xs) + change_coins(amount - x, [x,*xs])
  end
end

change_coins 11, [1, 2, 5]           # => 11
change_coins 2, [3]                  # => 0
change_coins 100, [1, 5, 10, 25, 50] # => 292

明智的回报值

  

例如,在这个问题中,如果金额不能由任何硬币组合弥补,我们必须返回-1。

-1案件很愚蠢。有方法只需3美分硬币即可改变2美分;因此我们返回0

如果你真的必须返回-1,只需使用一个愚蠢的包装

def cc amount, xs
  count = change_coins amount, xs
  if count == 0 then -1 else count end
end

订单无关紧要

change_coins 11, [5, 1, 2]           # => 11
change_coins 2, [3]                  # => 0
change_coins 100, [50, 1, 25, 10, 5] # => 292