【问题标题】:Coin change problem with infinite number of coins of each denomination每种面额的硬币数量无限的硬币找零问题
【发布时间】:2009-10-05 04:58:58
【问题描述】:

我想知道硬币找零问题的算法思想,其中每个面额都有无限数量的硬币。表示如何申请DP(如标准硬币找零问题) 例如在第 1,10,15 组中, 找 35 给--2 个 10 硬币和 1 个 15 硬币

还给我一个暴力强制算法的想法。我知道迭代所有的集合。但是如何在暴力破解时改变每个硬币的数量

【问题讨论】:

标签: algorithm coin-change


【解决方案1】:

我会考虑逐步构建解决方案,以归纳方式:

可用的硬币有 1c、5c、10c、25c(您可以根据需要调整它们)

  1. 1c = 1 X 1c 的最小硬币。最多 4 美分,我们需要 1c 硬币,因为这是最小的面额。
  2. 5 美分,我们有一个 5c 硬币。结合上面的 4c,我们可以生成 1 到 9 之间的任意数字。
  3. 对于 10 美分,我们需要 1 X 10c。结合以上三者,我们可以生成 1 到 19 之间的任意数字。
  4. 对于 20c,我们需要 2 x 10c,因为 20 可以被 10 整除。

如果你能归纳出问题,解决它可能会更容易。

编辑:
好吧,这里再尝试解释一下动态规划解决方案:

想象一张表,其中有x 行(x 是不同面额的数量)和n 列(n 是您必须使用最少面额构建的数量)。此表中的每个单元格都代表一个不同的子问题,最终将包含它的解决方案。假设:

第 1 行代表集合 {1c},即在第 1 行中,您可以使用无限 1c
第 2 行表示集合 {1c, 10c},即在第 2 行中,您可以无限 1c10c
第 3 行代表集合 {1c, 10c, 15c} 等等...
每列代表您要构建的数量。

因此,每个单元格对应一个小子问题。例如(为简单起见,索引从 1 开始),
cell(1, 5) ==> 构造 5c 仅使用 {1c}
cell(2, 9) ==> 构造 9c 使用 @987654336 @
cell(3, 27) ==> 使用{1c, 10c, 15c}构造27c
现在你的目标是得到cell(x, n)的答案

Solution:
从最简单的问题开始解决表格。解决第一行是微不足道的,因为在第一行中唯一可用的面额是{1c}。第 1 行中的每个单元格都有一个简单的解决方案,导致 cell(1, n) = {nx1c}n 1c 的硬币)。

现在继续下一行。概括第二行,让我们看看如何解决(比如说)cell(2, 28),即使用{1c, 10c} 构造28c。在这里,您需要做出决定,是否在解决方案中包含10c,以及有多少硬币。你有 3 个选择 (3 = 28/10 + 1)

Choice 1:
{1x10c} 和前一行的其余部分(存储在cell(1, 18) 中)。这给了你{1x10c, 18x1c} = 19 coins

Choice 2:
{2x10c} 和前一行的其余部分(存储在cell(1, 8) 中)。这给了你{2x10c, 8x1c} = 10 coins

Choice 3:
不取10c 和前一行的其余部分(存储在cell(1, 28) 中)。这给了你{28x1c} = 28 coins

显然,选择 2 是最好的,因为它需要更少的硬币。把它写在桌子上,然后继续。表格将一次填满一行,并且在一行内按数量递增的顺序填满。

按照上述规则,您将到达cell(x, n),解决方案将是在n/p + 1 替代方案之间进行选择,其中p = 行中的最新面额x。最好的选择就是你的答案。

表格实际上是记住了小问题的解决方案,这样你就不需要一次又一次地解决它们。

【讨论】:

  • 我理解了你答案的第一部分(通过归纳)。但是你给出的代码给出的答案是 7。但最佳是 3
  • 我想我犯了一个错误。删除答案的代码部分以避免混淆。
  • 呸!凌晨 4:30 的帖子很长。
  • 希望我的帖子能澄清你的疑惑。
  • 对于 [1,10,20,25],如果你想给 30cents,归纳如何工作?在 25cent 时,我们有最优解 25cent 硬币,在 29 时我们有 25+1+1+1+1,在 30cent 时会触发什么,我们应该放弃 25cent 硬币并开始使用 20cent 硬币?
【解决方案2】:

关于蛮力部分:

int i,j,k;
for(i=0;i<35;i++){
  for(j=0;j<4;j++){
    for(k=0;k<3;k++){
      if(1*i+10*j+15*k == 35){
        //is this what you need?
        //minimum=min(minimum,(i+j+k));
      }
    }
  }
}

【讨论】:

    【解决方案3】:

    这是将数字从一种编号系统转换为另一种编号系统的方法。例如:

    35 = 1*2^5 + 0*2^4 + 0*2^3 + 0*2^2 + 0*2^1 + 1*2^0
    

    即:

    var cash = 35;
    var coins = [15, 10, 5, 1];
    var change = {};
    for(var i=0; i<coins.length; i++){
      change[coins[i]]  = Math.floor(cash/coins[i]);
      cash %= coins[i];
    }
    //change now contains:
    //15:2, 10:0, 5:1, 1:0
    

    【讨论】:

    • 这并不总是最优的。例如,如果硬币是 [1, 3, 4],现金 = 6,您的程序将输出 4, 1, 1 而不是 3, 3。
    【解决方案4】:

    关于蛮力。

    它被称为“贪心算法”——你总是取最大的硬币,它不大于 你需要代表的价值。

    伪代码,返回代表价值所需的硬币数量,如果我们每个硬币可以无限次使用

    int[] greedy(int value, int[] coins) {
       int[] ans = ...;
       int v = coins.length - 1;
       int left = value;
       while (left > 0 && v >= 0) {
           if (coins[v] <= left) {
               ans.push(coins[v]);
           } else { 
               v--;
           }
       }
       return left == 0 ? ans : //representation impossible, 
                                //so you better do something;
    }
    

    伪代码,返回代表价值所需的硬币数量,如果我们每个硬币可以无限次使用

    int f(int value, int[] coins) {
       int[] memo = new int[value + 1];
       Arrays.fill(memo, 1234567);
       memo[0] = 0;
       for (int coin : coins)
           for (int i = 0; i + coin <= value; i++)
               memo[i + coin] = min(memo[i + coin], memo[i] + 1);
       return memo[value];
    }
    

    要知道要拿哪些硬币,请从末尾开始:if memo[value] = 3,然后检查所有硬币并找到memo[value - coin] == 2的硬币,从(value - coin)继续直到到达0

    【讨论】:

    • !蛮力不是贪婪的。蛮力就是这样。蛮力。在蛮力中,您模拟每种可能的组合并选择优化某些功能的组合。贪婪,会尝试一次做一个决定,你不会回溯到最后导致一个单一的组合..
    【解决方案5】:

    你可以在这里运行它http://www.exorithm.com/algorithm/view/coin_change

    function coin_change ($amount, $coins)
    {
      $change = array();
      rsort($coins);
      for($i=0; $i<count($coins); $i++) {
        $change[$coins[$i]] = floor($amount/$coins[$i]);
        $amount = $amount % $coins[$i];
      }
      return $change;
    }
    

    【讨论】:

    • 对于零钱 30 和硬币 [1,10,20,25] 这将给出错误/次优结果,因为硬币 = 6
    猜你喜欢
    • 1970-01-01
    • 2019-07-17
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 2018-11-05
    • 1970-01-01
    • 2013-12-09
    相关资源
    最近更新 更多