【问题标题】:DP - Counting coin changeDP - 数硬币找零
【发布时间】:2012-08-05 08:35:02
【问题描述】:

问题需要计算特定成本的硬币变化次数。

例如,如果我的硬币值为50, 20, 10, 5, 1,我可以形成以下成本:

5 => (5), (11111), 2 种方式。

10 => (10), (5, 5), (5, 11111), (11111, 11111),这四种方式。

这是我的功能。它从 10 的成本乞求返回错误的结果(返回 9 种方式,而实际的方式只有 4 种)

int dp[10000];
int coins[] = { 50, 20, 10, 5, 1 };
int rec(int n)
{
  if (n == 0) return 1;
  if (dp[n] != -1) return dp[n];
  int cnt = 0;
  for (int i = 0; i < 5; i++)
    if (coins[i] <= n) cnt += rec(n - coins[i]);
  return dp[n] = cnt;
}

如何修复此功能以提供正确数量的方式?这个算法是否正确?查看完整代码及其输出here

注意:我的问题不在于 dp 数组初始化。每次调用rec之前,我都使用memset将其初始化为-1

【问题讨论】:

  • 到底是什么问题(你想要得到什么,你实际得到什么)?
  • 您的代码给出的结果是什么?

标签: c++ algorithm dynamic-programming coin-change


【解决方案1】:

(5, 1, 1, 1, 1, 1) 和 (1, 1, 1, 5, 1, 1) 在你的算法中是不同的方式,你应该让它减少。

int dp[10000][5];  // dp[20][2] means, if the biggest coin is coins[2],
                   // how much ways for 20 ?
int coins[] = { 1, 5, 10, 20, 50 }; // here
int rec(int n, int m)
{
  int cnt = 0;
  int i;
  if (n == 0) return 1;
  //if (m == 0) return 1;
  if (dp[n][m] != -1) return dp[n][m];
  for (i = 0; i <= m; i++)
    if (coins[i] <= n) cnt += rec(n - coins[i], i);
  return dp[n][m] = cnt;
}

int main()
{
        memset(dp, -1, sizeof(dp));
        printf("%d\n", rec(10, 4));
}

【讨论】:

  • 你为什么用m = 4来称呼它?
  • 他交换了订单。他让硬币从小到大,for循环从0到m,所以和我的一样,只是格式不同……
  • @Lai 并没有什么不同。很抱歉我忘记说明了。该算法需要计数 distinct 种方式。
【解决方案2】:

结果是错误的,因为您从未确保您的算法从 5 个硬币开始。 (5,11111) 在您的代码中与 (1, 5, 1111) 一样有效,但结果相同。你的结果应该是错误的 6 和更高,而不是 10 和更高。

要解决此问题,您可以在函数 rec() 中进行截断:

int rec(int n, int cutoff)
{
  if (n == 0) return 1;
  if (dp[n] != -1) return dp[n];
  int cnt = 0;
  for (int i = cutoff; i < 5; i++)
    if (coins[i] <= n) cnt += rec(n - coins[i], i);
  return dp[n] = cnt;
}

应该这样做。

编辑:你将不得不照顾你的 dp[] 数组,因为它不关心这个截止,但这通常是你遇到的错误。您可以评论该行,并检查它是否有效。

【讨论】:

  • 那么如何以 n = 10 为例呢?
  • 第一次调用必须使用 rec(10, 0)。我已经编辑了你的 dp[] 数组会搞砸该算法,所以为了确保这个想法有效,你应该评论 if(dp[n]!=-1)...
  • 我可以制作dp 二维以使用截止吗?取消评论时不起作用
  • 是的,这就是解决这个问题的方法。第二个元素应显示允许的最大数量。就像 Lai 写的那样,我只是想说明错误来自哪里。
【解决方案3】:

一句话:你的初始化

memset(dp, -1, sizeof dp);

并不安全。 memset 初始化内存空间的每个字节(参见http://www.cplusplus.com/reference/clibrary/cstring/memset/。)。对于这种特殊情况,您很幸运,int(-1) 的表示(可能)与unsigned char(-1) 的四倍相同。

我建议使用std::fill (http://www.cplusplus.com/reference/algorithm/fill/)。

【讨论】:

    猜你喜欢
    • 2013-12-09
    • 1970-01-01
    • 2016-01-25
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多