当前位置: 首页 > news >正文

【数据结构与算法Trip第5站】动态规划

动态规划详解(含C++代码实现)

什么是动态规划?

动态规划(Dynamic Programming,简称DP)是一种解决复杂问题的算法思想,它通过将问题分解为相互重叠的子问题,并存储子问题的解(记忆化)来避免重复计算,从而提高效率。

动态规划的核心思想

  1. 最优子结构:问题的最优解包含其子问题的最优解
  2. 重叠子问题:不同的决策序列会到达相同的子问题
  3. 状态转移方程:定义如何从一个状态转移到另一个状态

动态规划的两种实现方式

1. 自顶向下(记忆化递归)

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;// 斐波那契数列 - 记忆化递归
unordered_map<int, int> memo;int fib(int n) {if (n <= 1) return n;if (memo.find(n) != memo.end()) return memo[n];memo[n] = fib(n - 1) + fib(n - 2);return memo[n];
}

2. 自底向上(迭代)

// 斐波那契数列 - 迭代
int fib_iterative(int n) {if (n <= 1) return n;vector<int> dp(n + 1);dp[0] = 0;dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];
}

经典动态规划问题示例

1. 爬楼梯问题

问题描述:每次可以爬1或2个台阶,爬到n阶有多少种方法?

int climbStairs(int n) {if (n <= 2) return n;vector<int> dp(n + 1);dp[1] = 1;dp[2] = 2;for (int i = 3; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];
}// 空间优化版本
int climbStairs_optimized(int n) {if (n <= 2) return n;int prev1 = 1, prev2 = 2;for (int i = 3; i <= n; i++) {int current = prev1 + prev2;prev1 = prev2;prev2 = current;}return prev2;
}

2. 0-1背包问题

问题描述:给定物品的重量和价值,在不超过背包容量的情况下最大化价值。

int knapsack(int W, vector<int>& weights, vector<int>& values) {int n = weights.size();vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));for (int i = 1; i <= n; i++) {for (int w = 1; w <= W; w++) {if (weights[i - 1] <= w) {dp[i][w] = max(dp[i - 1][w],  // 不选当前物品dp[i - 1][w - weights[i - 1]] + values[i - 1]  // 选当前物品);} else {dp[i][w] = dp[i - 1][w];}}}return dp[n][W];
}// 空间优化版本(一维数组)
int knapsack_optimized(int W, vector<int>& weights, vector<int>& values) {int n = weights.size();vector<int> dp(W + 1, 0);for (int i = 0; i < n; i++) {for (int w = W; w >= weights[i]; w--) {dp[w] = max(dp[w], dp[w - weights[i]] + values[i]);}}return dp[W];
}

3. 最长公共子序列(LCS)

问题描述:找出两个序列的最长公共子序列的长度。

int longestCommonSubsequence(string text1, string text2) {int m = text1.length(), n = text2.length();vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {if (text1[i - 1] == text2[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + 1;} else {dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}}return dp[m][n];
}

动态规划解题步骤

  1. 定义状态:明确dp数组的含义
  2. 确定状态转移方程:如何从已知状态推导出新状态
  3. 初始化基础情况:最简单的子问题的解
  4. 确定计算顺序:确保计算当前状态时所需的状态已计算
  5. 返回结果:根据问题要求返回相应的结果

复杂度分析

  • 时间复杂度:通常为O(状态数 × 每个状态的计算时间)
  • 空间复杂度:通常为O(状态数),可通过优化降低

适用场景

动态规划适用于具有以下特征的问题:

  • 求最优解(最大值、最小值等)
  • 问题可以分解为相互重叠的子问题
  • 具有最优子结构性质

总结

动态规划是一种强大的算法设计技术,通过练习各种经典问题(如背包问题、最长递增子序列、编辑距离等),可以更好地掌握这种思想。关键是多实践,理解状态定义和转移方程的设计。

http://www.dtcms.com/a/393184.html

相关文章:

  • 防抖那些事儿
  • 【办公类-115-01】20250920信息员每周通讯上传之文字稿整理
  • 深入解析HotSpot解释器方法调用机制:从invokevirtual到方法入口
  • 用AI修复失语者的声音:大模型如何帮助渐冻人重新“说话”?
  • 【ICCV 2023】通过学习采样来学习上采样
  • 有效解决舍入误差的方法
  • count down 90 days
  • GEO完全指南 AI时代的内容优化新范式
  • Npass gate transistor是什么器件?
  • TensorRT-LLM中的in-flight batching(IFB)
  • kaggle-NeurIPS - Open Polymer Prediction 2025-0.069
  • Linux基础网络命令
  • CTFHub 密码口令通关笔记:默认密码
  • IPv6 网络连接与 “v6ns“ 测试失败解决方案
  • C++11之move移动语义
  • 【计算机三级备考】真题总结
  • Linux基础指令(上)
  • Protocol Buffers .NET 运行时从核心 API 到工程实战
  • DFT教程 part1 VASP安装与学习推荐
  • 泛函 Φ(u) 驻点所满足的偏微分方程与自然边界条件
  • 基于springboot的健康饮食营养管理系统
  • C语言入门指南:联合体与枚举
  • JS逆向 -去哪儿滑块
  • C++包装器(Wrapper)概述
  • java后端工程师进修ing(研一版‖day47)
  • 小谈:物联网(IoT)与工业传感器技术
  • python标准库
  • 01 Tasking软件安装及新建工程
  • ​​[硬件电路-288]: 单路双输入异或门(门控开关:两个同时为1,输出为1)NC7SZ86L6X 功能概述与管脚定义
  • 声明式事务4