动态规划(DP)通过分解成子问题解决了给定复杂的问题,并存储子问题的结果,以避免再次计算相同的结果。我们通过下面这个问题来说明这两个重要属性:
1)重叠子问题
2)最优子结构
1)重叠子问题:
像分而治之,动态规划也把问题分解为子问题。动态规划主要用于:当相同的子问题的解决方案被重复利用。在动态规划中,子问题解决方案被存储在一个表中,以便这些不必重新计算。因此,如果这个问题是没有共同的(重叠)子问题, 动态规划是没有用的。例如,二分查找不具有共同的子问题。下面是一个斐波那契函数的递归函数,有些子问题被调用了很多次。
1 |
/* simple recursive program for Fibonacci numbers */ |
2 |
int fib( int n)
|
3 |
{ |
4 |
if ( n <= 1 )
|
5 |
return n;
|
6 |
return fib(n-1) + fib(n-2);
|
7 |
} |
执行 fib(5) 的递归树
1 |
fib(5)
|
2 |
/ \
|
3 |
fib(4) fib(3)
|
4 |
/ \ / \
|
5 |
fib(3) fib(2) fib(2) fib(1)
|
6 |
/ \ / \ / \
|
7 |
fib(2) fib(1) fib(1) fib(0) fib(1) fib(0)
|
8 |
/ \
|
9 |
fib(1) fib(0) |
我们可以看到,函数f(3)被称执行2次。如果我们将存储f(3)的值,然后避免再次计算的话,我们会重新使用旧的存储值。有以下两种不同的方式来存储这些值,以便这些值可以被重复使用。
A)记忆化(自上而下):
B)打表(自下而上):
一)记忆化(自上而下):记忆化存储其实是对递归程序小的修改,作为真正的DP程序的过渡。我们初始化一个数组中查找所有初始值为零。每当我们需要解决一个子问题,我们先来看看这个数组(查找表)是否有答案。如果预先计算的值是有那么我们就返回该值,否则,我们计算该值并把结果在数组(查找表),以便它可以在以后重复使用。
下面是记忆化存储程序:
01 |
/* Memoized version for nth Fibonacci number */ |
02 |
#include<stdio.h> |
03 |
#define NIL -1 |
04 |
#define MAX 100 |
05 |
06 |
int lookup[MAX];
|
07 |
08 |
/* Function to initialize NIL values in lookup table */ |
09 |
void _initialize()
|
10 |
{ |
11 |
int i;
|
12 |
for (i = 0; i < MAX; i++)
|
13 |
lookup[i] = NIL;
|
14 |
} |
15 |
16 |
/* function for nth Fibonacci number */ |
17 |
int fib( int n)
|
18 |
{ |
19 |
if (lookup[n] == NIL)
|
20 |
{
|
21 |
if ( n <= 1 )
|
22 |
lookup[n] = n;
|
23 |
else
|
24 |
lookup[n] = fib(n-1) + fib(n-2);
|
25 |
}
|
26 |
27 |
return lookup[n];
|
28 |
} |
29 |
30 |
int main ()
|
31 |
{ |
32 |
int n = 40;
|
33 |
_initialize();
|
34 |
printf ( "Fibonacci number is %d " , fib(n));
|
35 |
getchar ();
|
36 |
return 0;
|
37 |
} |
一)打表(自下而上)
下面我们给出自下而上的打表方式,并返回表中的最后一项。
01 |
/* tabulated version */ |
02 |
#include<stdio.h> |
03 |
int fib( int n)
|
04 |
{ |
05 |
int f[n+1];
|
06 |
int i;
|
07 |
f[0] = 0; f[1] = 1;
|
08 |
for (i = 2; i <= n; i++)
|
09 |
f[i] = f[i-1] + f[i-2];
|
10 |
11 |
return f[n];
|
12 |
} |
13 |
14 |
int main ()
|
15 |
{ |
16 |
int n = 9;
|
17 |
printf ( "Fibonacci number is %d " , fib(n));
|
18 |
getchar ();
|
19 |
return 0;
|
20 |
} |
这两种方法都能存储子问题解决方案。在第一个版本中,记忆化存储只在查找表存储需要的答案。而第二个版本,所有子问题都会被存储到查找表中,不管是否是必须的。比如LCS问题的记忆化存储版本,并不会存储不必要的子问题答案。
ACM之家原创,文章链接:http://www.acmerblog.com/dynamic-programming-4577.html
相关推荐
动态规划是一种有效的算法设计策略,尤其适用于解决具有重叠子问题和最优子结构的问题,而0-1背包问题就是这种策略的一个典型应用。0-1背包问题是一个经典的组合优化问题,它源自实际生活中的资源分配问题,比如在...
0-1背包问题是一种经典的组合优化问题,在计算机科学和算法设计中...通过理解并掌握动态规划的思想,我们可以解决一系列具有重叠子问题和最优子结构性质的问题,这对于提升编程能力和解决实际问题的能力都非常重要。
### 动态规划算法的基本思想 #### 一、动态规划简介 ...通过利用问题的最优子结构性质和子问题重叠性质,动态规划能够高效地解决问题。在实际应用中,选择合适的状态定义和递归关系是设计动态规划算法的关键。
动态规划是一种有效的方法,它适用于具有最优子结构和子问题重叠性质的问题。在本实验中,我们将探讨动态规划的基本思想、问题分析、时空复杂度以及实际的算法实现。 动态规划的核心思想是通过分解问题为更小的子...
3. **贪心选择性质**:在动态规划中,贪心策略不一定适用,但在某些问题中,结合动态规划和贪心策略可能更高效。 **总结** 动态规划算法通过合理地定义状态和状态转移,有效地解决了许多复杂问题。它鼓励我们思考...
动态规划是一种重要的算法思想,广泛应用于解决复杂的问题,如矩阵连乘问题、电路布线问题、0-1背包问题等。动态规划的核心在于利用最优子结构和重叠子问题的特性,通过自底向上的方式逐步求解问题,避免了重复计算...
动态规划解决问题的关键在于找到问题的最优子结构和重叠子问题这两个特性。 #### 最优子结构性质 最优子结构性质指的是一个问题的最优解包含其子问题的最优解。在矩阵链乘法问题中,假设存在一种最佳的方式去括号化...
总结来说,0-1背包问题是一个典型的动态规划问题,其核心在于利用最优子结构和重叠子问题的特性,通过递归公式构建并填充一个二维数组,以找到背包容量限制下的最大价值解决方案。在实现过程中,通过跳跃点优化可以...
2. 重叠子问题性质:子问题之间存在重叠,避免重复计算。 动态规划算法的步骤包括: 1. 分析最优解的结构 2. 给出计算局部最优解值的递归关系 3. 自底向上计算局部最优解的值 4. 根据最优解的值构造最优解 动态...
问题重叠性质是指,每次产生的子问题并不总是新问题,利用动态规划对每一个子问题只解一次,并将其存入表格中,下次用到该子问题的解时,只要查找表格即可。 动态规划算法是解决编辑距离问题的有效方法,可以提高...
总结来说,动态规划是一种强大的算法设计策略,尤其适用于存在重叠子问题和最优子结构的问题。它通过自底向上的计算和存储子问题解,有效地减少了计算复杂性,解决了许多实际问题,如最短路径、背包问题、最长公共子...
* 掌握动态规划算法的基本要素,包括最优子结构性质和重叠子问题性质。 * 掌握设计动态规划算法的步骤,包括找到最优解的性质、递归地定义最优值、以自底向上的方式计算出最优值和根据计算最优值时得到的信息构造最...
- 动态规划的应用实例,如斐波那契数列、最长公共子序列、0-1背包问题等 - 动态规划的状态定义和状态转移矩阵 - 记忆化搜索的实现方式和效率分析 - 自底向上和自顶向下的算法设计 - 动态规划与其他算法(如分治、...
- 有重叠子问题和最优子结构的问题。 - 当问题可以通过逐步构建完整解来解决时。 理解动态规划的关键在于识别问题的状态、状态转移方程,并有效地存储和利用子问题的解。通过熟练掌握动态规划,可以解决许多复杂...
与其他解决问题的思想相比,如递归和分治,动态规划的差异在于它能够更高效地处理具有重叠子问题的情况。递归通过函数自身反复调用,将问题分解为更小的子问题,但可能会出现大量重复计算。分治法则是将大问题拆分为...
动态规划的核心在于它的两个关键性质:最优子结构性质和子问题重叠性质。 1. 最优子结构:这是动态规划的基础,意味着一个问题的全局最优解包含了其子问题的最优解。如果一个问题满足最优子结构,那么我们可以从...
动态规划的要素包括最优子结构和重叠子问题。 贪心算法 贪心算法是一种在解决问题的策略上目光短浅,只根据当前已有的信息就做出选择,并且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。换言之,贪心...
动态规划问题解析 动态规划是一种非常重要的算法设计技术,旨在解决复杂问题的优化问题。它将问题分解成小问题,解决小问题,然后组合小问题的解决方案来解决整体问题。动态规划的应用非常广泛,例如解决最长公共子...
8. **典型问题**:经典的动态规划问题包括:斐波那契数列、最长公共子序列、最长上升子序列、编辑距离、矩阵链乘法、背包问题、最短路径问题等。 9. **状态转移方程**:每个动态规划问题都对应着一个状态转移方程,...
- 在某些情况下,动态规划问题可以转化为线性规划问题求解,反之亦然。 - 这种转化能提供更有效的求解策略,如用DP解决LP的松弛问题。 7. **源代码实现** - 提供的源代码可能涵盖以上各种规划问题的实现,通常...