今日分享 动态规划
一、动态规划核心概念
动态规划是一种通过拆分问题、存储子问题答案来避免重复计算,从而高效解决多阶段决策问题的算法思想,核心是“备忘录”(存储子问题结果)和“状态转移”(子问题间的推导关系)。
1. 适用场景
问题可拆分为多个“子问题”,且子问题结果会重复使用(重叠子问题)。
问题的最优解可由子问题的最优解推导得出(最优子结构)。
2. 解题步骤
1. 定义状态:用 dp[i] 或 dp[i][j] 表示“到第i步/第i,j个位置的最优解/结果”,明确状态含义是关键。
2. 推导状态转移方程:确定 dp[i] 与 dp[i-1] 、 dp[i-2] 等前序状态的关系(核心公式)。
3. 初始化状态:给最基础的子问题(如 dp[0] 、 dp[1] )赋值,作为推导起点。
4. 计算最终结果:按顺序(从基础子问题到目标问题)计算,得到最终答案。
二、例题:爬楼梯
题目描述
假设你正在爬楼梯,需要 n 阶才能到达楼顶。每次可以爬1或2个台阶,有多少种不同的方法可以爬到楼顶?
问题分析
状态定义: dp[i] 表示“爬到第i阶台阶的不同方法数”。
状态转移方程:要到第 i 阶,前一步只能是“从第i-1阶爬1阶”或“从第i-2阶爬2阶”,因此 dp[i] = dp[i-1] + dp[i-2] 。
初始化:
dp[1] = 1 (1阶台阶只有1种方法:爬1阶)。
dp[2] = 2 (2阶台阶有2种方法:1+1或2)。
最终结果: dp[n] 。
三、多语言实现
1. C语言实现
#include <stdio.h>
int climbStairs(int n) {
// 优化空间:无需数组,用两个变量存储前两个状态
if (n == 1) return 1;
if (n == 2) return 2;
int prev_prev = 1; // dp[i-2]
int prev = 2; // dp[i-1]
int curr; // dp[i]
for (int i = 3; i <= n; i++) {
curr = prev_prev + prev;
prev_prev = prev;
prev = curr;
}
return prev;
}
int main() {
int n = 5;
printf("爬%d阶楼梯的方法数:%d\n", n, climbStairs(n)); // 输出8
return 0;
}
2. C++语言实现
#include <iostream>
using namespace std;
int climbStairs(int n) {
if (n <= 2) return n;
int dp_prev2 = 1, dp_prev1 = 2, dp_curr;
for (int i = 3; i <= n; ++i) {
dp_curr = dp_prev2 + dp_prev1;
dp_prev2 = dp_prev1;
dp_prev1 = dp_curr;
}
return dp_prev1;
}
int main() {
int n;
cout << "请输入台阶数n:";
cin >> n;
cout << "方法数:" << climbStairs(n) << endl;
return 0;
}
3. Python语言实现
def climb_stairs(n):
if n <= 2:
return n
# 初始化前两个状态
prev_prev, prev = 1, 2
for _ in range(3, n + 1):
curr = prev_prev + prev
prev_prev, prev = prev, curr
return prev
# 测试
n = 5
print(f"爬{n}阶楼梯的方法数:{climb_stairs(n)}") # 输出8
4. Java语言实现
import java.util.Scanner;
public class ClimbStairs {
public static int climbStairs(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
int dpPrev2 = 1; // dp[i-2]
int dpPrev1 = 2; // dp[i-1]
int dpCurr = 0;
for (int i = 3; i <= n; i++) {
dpCurr = dpPrev2 + dpPrev1;
dpPrev2 = dpPrev1;
dpPrev1 = dpCurr;
}
return dpCurr;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入台阶数n:");
int n = scanner.nextInt();
System.out.println("方法数:" + climbStairs(n));
scanner.close();
}
}
四、代码说明
所有实现均采用空间优化:原本需用长度为 n 的数组存储 dp ,但因 dp[i] 仅依赖 dp[i-1] 和 dp[i-2] ,故用两个变量替代数组,空间复杂度从 O(n) 降至 O(1) ,时间复杂度均为 O(n) (仅需遍历1次从3到n的台阶)。