DP - 计算硬币变化

时间:2012-08-08 08:56:55

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

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

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

5 => (5),(11111),这是两种方式。

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

这是我的功能。它从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数组初始化。我在调用memset之前每次使用-1将其初始化为rec

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

结果是错误的,因为你永远不会确保你的算法以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 []数组,因为它不关心这个截止,但这通常是你遇到的错误。您可以对该行进行评论,并检查是否有效。

答案 2 :(得分:1)

一句话:你的初始化

memset(dp, -1, sizeof dp);

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

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