我正在浏览一些算法,遇到了coin change问题。
在思考这个问题时,我想出了这个天真的递归解决方案:
mostrarOpcionesUsuario
然后我意识到"接受"解决方案如下:
int coinChange(const vector<int>& coins, int start, int n) {
if (n == 0) return 1;
if (n < 0) return 0;
int total = 0;
for (int i = start; i < coins.size(); ++i) {
if (coins[i] <= n) total += coinChange(coins, i, n-coins[i]);
}
return total;
}
起初我以为这两者基本相同。我很清楚,我的递归树要宽得多,但似乎这只是因为我的算法在每个级别都做了更多的工作,所以它平平了。看起来两种算法都在考虑使用当前硬币进行更改的方式的数量(假设它是&lt; =当前总和),并且考虑在没有当前硬币的情况下进行更改的方式的数量(因此具有所有元素)硬币阵列减去当前硬币)。因此,我的算法中的参数int count( int S[], int m, int n )
{
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
return 1;
// If n is less than 0 then no solution exists
if (n < 0)
return 0;
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
return 0;
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}
与start
在第二种算法中所做的基本相同。
我看的越多,似乎无论以前的文字如何,我的算法都是m
,第二个是O(n^n)
。我已经看了太长时间,但是如果有人能解释我的算法正在做的额外工作与第二个那么好的工作。
我理解这个问题的动态编程解决方案,这个问题纯粹是一个基于复杂性的问题。
答案 0 :(得分:3)
这两段代码是相同的,除了第二段代码使用递归而不是for循环迭代硬币。这使得它们的运行时复杂性相同(尽管由于额外的递归调用,第二段代码可能具有更差的内存复杂性,但这可能会在清洗中丢失)。
例如,在S = [1,5,10]和m = 3的情况下,这里对第二count
进行部分评估。在每一行上,我展开了count
的最左边定义。
count(S, 3, 100)
= count(S, 2, 100) + count(S, 3, 90)
= count(S, 1, 100) + count(S, 2, 95) + count(S, 3, 90)
= count(S, 0, 100) + count(S, 1, 99) + count(S, 2, 95) + count(S, 3, 90)
= 0 + count(S, 1, 99) + count(S, 2, 95) + count(S, 3, 90)
您可以看到这与总和total
的for循环的计算方法相同。
两种算法都很糟糕,因为它们在指数时间内运行。这是一个答案(我的)使用一个在O(nm)时间运行并使用O(n)内存的简洁动态编程方法,并且非常简洁 - 大小与您的天真递归解决方案相当。 https://stackoverflow.com/a/20743780/1400793。它在Python中,但它可以简单地转换为C ++。
答案 1 :(得分:1)
你没看过整篇文章(?)。
动态编程背后的理念是,您存储了一些已经存在的值,并且您不需要再次计算它们。在文章的最后,您可以看到实际的正确解决方案。
至于为什么你的解决方案是n ^ n而它们的原始解是2 ^ n。两种解决方案实际上都是2 ^(n +#币)。他们只是用m-1调用函数,而不是通过每个硬币循环。虽然你的解决方案在开始时尝试每个硬币然后越来越少,他们试图拿一个m型硬币,然后是另一个硬币,然后是另一个硬币,直到在某个时刻它切换到输入m-1并对它做同样的事情。上。基本上两种解决方案都是一样的。
另一种证明它们具有相同复杂性的方法是这样的:
两种解决方案都是正确的,因此它们将达到所有可能的解决方案,并且一旦达到负n,它们都会停止增长递归的特定分支。因此,它们具有相同的复杂性。
如果您不相信只是尝试每个解决方案,除了添加一些计数器并在每次进入该功能时递增它。为每个解决方案执行此操作,您将看到相同的数字。
答案 2 :(得分:0)
<强>基准强> 在我的计算机基准测试中如下:
coinChange(v, 0, 500);// v=[1, 5, 10, 15, 20, 25]
需要1.84649才能完成。 但
count(s, 6, 500); //s = [1, 5, 10, 15, 20, 25]
执行0.853075s。
的修改
我将结果解释为两个算法的时间复杂度相同。