我正在从一本书中学习动态编程解决方案。我了解问题的解决方案,但不确定该书没有提供解决方案的时间复杂性。
我的问题:
给出无限个四分之一(25美分),一角钱(10美分),镍(5美分)和便士(1美分),编写代码以计算表示n美分的方式。
我的分析
每个输入必须经过4个“级别”,即25、10、5,1。 在第一级中,循环将执行n / 25次;在第二级中,循环将执行最多(n / 25)(n / 10)次,而第三级则最多执行(n / 25)(n / 10)(n / 5),最后一级最多执行(n / 25)(n / 10)*(n / 5) n。所以总运行时间为(n / 25)(n / 10)*(n / 5) n +(n / 25)(n / 10)(n / 5)+(n / 25)(n / 10)+ n / 25,即O(N ^ 4)。首先,我不确定我的归纳是否正确。其次,如果我是对的,我想知道是否存在更紧密的联系,因为在每个级别中,我仅计算最大次数而不是平均次数。
解决方案如下:
int makeChange(int n) {
int[] denoms = {25, 10, 5, l};
int[][] map = new int[n + l][denoms.length]; // precomputed
vals
return makeChange(n, denoms, 0, map);
}
int makeChange(int amount, int[] denoms, int index, int[][] map) {
if (map[amount][index] > 0) {//retrieve value
return map[amount][index];
}
if (index >= denoms.length - 1) return 1; // one denom remaining
int denomAmount denoms[index];
int ways = 0;
for (int i= 0; i * denomAmount <= amount; i++) {
//go to next denom, assuming i coins of denomAmount
int amountRemaining = amount - i * denomAmount;
ways += makeChange(amountRemaining, denoms, index + 1,
map);
}
map[amount][index] = ways;
return ways;
}
答案 0 :(得分:2)
编写的算法为O(n 2 )。分析递归函数时,可以将它们分为两部分:
然后,只需将这两个数字相乘即可。因为函数结果被缓存在这里,所以缓存中每个值的工作最多只能完成一次。由于缓存的大小为O(n),因此需要O(n)的时间来填充。
对于每个函数完成的工作,有一个while循环经历O(n)迭代。将它们相乘即可得出O(n 2 )的估计值,这是通过进行粗略估计得出的(输入值加倍会导致所需时间大约翻四倍)。
答案 1 :(得分:0)
只需递归考虑即可计算复杂度。为此,只需考虑25个硬币的数量,然后再考虑其他数量。如果T(n,i)
显示了以n
的最后i
个数表示denoms
的方式数目,则我们将得到T(n,4) = T(n-25, 3) + T(n - 2 * 25, 3) + ... + T(n - n//25 * 25, 3)
(n//25
表示n
的整数除法。现在我们可以分别对10
,5
和1
重复此操作。
因此,要找到复杂性的严格界限,可以假设n
被25
,10
和5
整除很多次才能得出最坏的情况和严格的分析。
因此,严格的分析是:
T(n,4) = sum_{i=1}^{n/25} T(n-i*25, 3)
T(n,3) = sum_{i=1}^{n/10} T(n-i*10, 2)
T(n,2) = sum_{i=1}^{n/5} T(n-i*5, 1)
T(n,1) = 1
现在,我们可以从下到上进行计算。 T(n,2) = Theta(n/5)
,T(n,3) = Theta(n/10 * n/5)
和T(n,4) = Theta(n/25 * n/10 * n/5) = Theta(n^3)
。如您所见,最终结果是Theta(n^3)
,并且渐近分析并未根据n
的确切值进行处理,或者被5
,10
或{{ 1}}。
此外,您在计算中存在错误,因为您没有考虑代码中的以下条件:
25
最后一次到if (index >= denoms.length - 1) return 1; // one denom remaining
的时间不正确,它是n
。