动态规划入门(一)
动态规划算法是试图求解无法用贪心法和分治法获得最有解的问题。
动态规划(Dynamic Programming,DP)是一项虽简单但较难掌握的技术。
动态规划的定义:
动态规划和备忘录共同作用。动态规划和分治法的主要区别是:对于后者,子问题是相互独立的,而在动态规划中子问题可能是重叠的,通过使用备忘录(用一个表来保存已解决子问题的答案),对于大部分问题,动态规划能够将带求解问题的复杂度由指数级降为多项式级。动态规划主要包含两部分:
递归:递归求解子问题。
备忘录:将已计算的值存储在表中。
动态规划= 递归 + 备忘录
动态规划的性质:
以下两条性质可用于判断动态规划方法是否可用于给定的问题:
最优子结构:问题的最优解包含其子问题的最优解。
子问题重叠:递归求解过程中包含少量不同子问题的多次重复计算。
动态规划的实现方法:
有两种基本的实现方法:
1.自底向上动态规划法
2.自顶向下动态规划法
动态规划算法的例子:
-------->许多字符串算法,如最长公共子序列、最长递增子序列,最长公共字串、编辑距离等
-------->关于图的有效求解算法:寻找图中最短距离的Bellman-Ford算法、Floyd的所有顶点间最短路径算法等。
-------->链矩阵乘法。
-------->子集和
-------->0/1背包问题
-------->旅行商问题
具体示例(java语言):
求解斐波那契数列。代码中使用了四种不同方法。
源代码:
import java.util.Scanner;
/**
* 动态规划算法
*/
public class Fibonacci {
public static void main(String[] args) {
Fibonacci fibonacci = new Fibonacci();
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int sum = fibonacci.recursiveFibonacci(n); // 递归实现
System.out.println("递归实现:" + sum);
int fib[] = new int[n + 1];
int sumOfFibo = fibonacci.fibo(n, fib);
System.out.println("自顶向下实现:" + sumOfFibo);
int sumOfFib = fibonacci.fib(n);
System.out.println("自底向上实现:" + sumOfFib);
int sumOfFibon = fibonacci.fibon(n);
System.out.println("改进算法:" + sumOfFibon);
}
// 递归实现(时间复杂度为:2的n次方)
public int recursiveFibonacci(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
return recursiveFibonacci(n - 1) + recursiveFibonacci(n - 2);
}
/*
* 动态规划算法实现之自底向上(时间复杂度为:O(n);空间复杂度为:O(n)):
* 从输入的最小参数值开始,逐步构建更大参数值的解
*/
public static int fib(int n) {
int[] fib = new int[n];
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n > 1) {
fib[0] = 1;
fib[1] = 1;
}
for (int i = 2; i < n; i++) {
fib[i] = fib[i - 1] + fib[i - 2]; // 动态规划,把值记录在备忘录中
}
return fib[n - 1];
}
/*
* 动态规划算法实现之自顶向下(时间复杂度为:O(n);空间复杂度为:O(n)):
* 在该方法中保留递归调用,如果某个子问题的解已计算则使用该值
*/
public int fibo(int n, int[] fib) {
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n == 2)
return 1;
if (fib[n] != 0) {
return fib[n];
}
return fib[n] = fibo(n - 1, fib) + fibo(n - 2, fib);
}
/*
* 进一步改进:进一步观察斐波那契数列可以发现:当前值仅仅是前两次计算结果之和。
* 这意味着不需要存储多有先前的返回值,而只需要存储最后两次计算的值就能计算出当前值。
* 该算法的时间复杂度为O(n),空间复杂度为:O(10)。
*/
public int fibon(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
int a = 0, b = 1, sum = -1, i;
for (i = 1; i < n; i++) {
sum = a + b;
a = b;
b = sum;
}
return sum;
}
}
运行结果截图:
PS:欢迎大家提出不同的看法或思路。欢迎大家来评论,一起学习交流进步。