问题需要计算特定成本的硬币更改次数。
例如,如果我的硬币值为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
。
答案 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::fill
(http://www.cplusplus.com/reference/algorithm/fill/)。