我理解为什么DP比简单递归更有效。
但是,我知道DP是使用memoization技术的递归。
对于Fibonacci,像这样:
int DP[10000]; //initalized by 0
int f(int num){
if (DP[num] != 0)
return DP[num];
DP[num] = f(num -1) + f(num - 2);
return DP[num];
}
我总是以这种递归方式使用DP,它在ACM等算法问题上运行良好。
最近,我知道大多数人都不会以这种方式使用DP。
他们像这样使用DP:
int fib(int n)
{
/* Declare an array to store Fibonacci numbers. */
int f[n+1];
int i;
/* 0th and 1st number of the series are 0 and 1*/
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
{
/* Add the previous 2 numbers in the series
and store it */
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
此来源来自http://www.geeksforgeeks.org/program-for-nth-fibonacci-number/
我不知道这两种方式的区别。
它们是否具有相同的时间复杂度?
另外,我想我的方式(递归+ memoization)可以称为DP吗?
使用我的DP方法对算法问题有什么不利之处吗?
答案 0 :(得分:5)
另外,我想我的方式(递归+ memoization)可以称为DP吗?
我认为这是一个临界案例。 Cormen等人的算法导论,第二版将您的方法(第347页)描述为“动态编程的变体”,将其与前面章节的“普通动态编程”进行对比,并将其称为“memoization”或“memoized recursion”。
使用我的DP方法对算法问题有什么不利之处吗?
在您的具体示例中,我认为最大的缺点是您需要O( n )堆栈空间,这可能是许多环境中的问题(堆栈空间通常比堆空间更有限) )。虽然您引用的普通DP版本仍然使用O( n )空间,但我认为如何从那里转到仅使用O(1)空间的版本更为明显。 (而且许多其他DP问题也是如此;例如,最大公共子序列问题只需要O( m + n )空间,但是一个memoized递归解决方案需要O( mn )空间。)
另一方面,在某些情况下,您的方法可能会更好,但实际上最终需要哪些子问题并不是那么明显。在这种情况下,memoized递归方法避免了解决不必要的情况。