动态规划(Dynamic Programming,简称DP)是一种用于解决复杂问题的算法思想,特别适用于具有重叠子问题和最优子结构性质的问题。它通过将问题分解为更小的子问题,并保存这些子问题的解以避免重复计算,从而提高算法的效率。动态规划通常用于优化问题,如最短路径、最长公共子序列、背包问题等。
动态规划的基本概念
-
重叠子问题(Overlapping Subproblems):
- 问题可以分解为多个子问题,这些子问题在解决过程中会重复出现。例如,计算斐波那契数列时,F(n) = F(n-1) + F(n-2),计算F(n-1)和F(n-2)时又会计算它们的前两个子问题。
-
最优子结构(Optimal Substructure):
- 问题的最优解可以通过子问题的最优解来构造。例如,最短路径问题中,从一个点到达另一个点的最短路径可以通过中间节点的最短路径来构造。
-
状态(State):
- 动态规划中的状态表示问题的一个具体子问题。例如,在斐波那契数列中,状态可以表示为F(n),即第n个斐波那契数。
-
状态转移方程(State Transition Equation):
- 描述如何通过子问题的解构造出原问题的解。例如,斐波那契数列的状态转移方程为F(n) = F(n-1) + F(n-2)。
动态规划的步骤
-
定义状态:
- 明确问题的状态,即子问题的定义。例如,对于斐波那契数列,状态可以定义为F(n),表示第n个斐波那契数。
-
确定状态转移方程:
- 根据问题的性质,找到状态之间的关系。例如,斐波那契数列的状态转移方程为F(n) = F(n-1) + F(n-2)。
-
初始化状态:
- 为一些基本状态赋初值。例如,斐波那契数列中,F(0) = 0 和 F(1) = 1。
-
计算状态:
- 按照状态转移方程计算出所有需要的状态值。
-
构造最优解:
- 根据计算出的状态值,得到问题的最优解。
动态规划的实现
动态规划可以通过两种方式实现:
-
自顶向下(带备忘录的递归,Memoization):
- 从顶层问题开始递归地解决每个子问题,并将已经计算过的子问题存储起来,以避免重复计算。
-
自底向上(迭代,Tabulation):
- 从最基本的子问题开始逐步计算出所有子问题的解,直到得到顶层问题的解。
示例:斐波那契数列
自顶向下(带备忘录的递归)
#include <iostream>
#include <vector>
using namespace std;unsigned long long fibonacciMemo(int n, vector<unsigned long long>& memo) {if (n <= 1) return n;if (memo[n] != -1) return memo[n];memo[n] = fibonacciMemo(n-1, memo) + fibonacciMemo(n-2, memo);return memo[n];
}unsigned long long fibonacci(int n) {vector<unsigned long long> memo(n+1, -1);return fibonacciMemo(n, memo);
}int main() {int n;cout << "Enter a number: ";cin >> n;cout << "Fibonacci(" << n << ") = " << fibonacci(n) << endl;return 0;
}
自底向上(迭代)
#include <iostream>
using namespace std;unsigned long long fibonacciDP(int n) {if (n <= 1) return n;unsigned long long prev2 = 0;unsigned long long prev1 = 1;unsigned long long curr;for (int i = 2; i <= n; ++i) {curr = prev1 + prev2;prev2 = prev1;prev1 = curr;}return curr;
}int main() {int n;cout << "Enter a number: ";cin >> n;cout << "Fibonacci(" << n << ") = " << fibonacciDP(n) << endl;return 0;
}
动态规划的应用
动态规划被广泛应用于许多经典算法问题中,如:
- 最短路径问题(如Dijkstra算法、Floyd-Warshall算法)
- 最长公共子序列(Longest Common Subsequence, LCS)
- 背包问题(Knapsack Problem)
- 股票买卖问题
- 编辑距离(Edit Distance)
- 矩阵链乘法
动态规划是解决这些问题的强大工具,通过合理地定义状态和状态转移方程,可以将许多复杂问题转化为可以有效解决的子问题。