动态规划模式

embedded/2025/1/7 19:52:10/

        动态规划(Dynamic Programming, DP)是一种解决复杂问题的算法设计技术,它通过将大问题分解为小问题,并利用小问题的解决方案来构造大问题的解决方案,从而避免了重复计算。动态规划通常用于具有“最优子结构”和“重叠子问题”特征的问题。

动态规划的基本步骤

  1. 定义状态:明确问题的状态表示。即如何用状态表示当前的子问题。
  2. 状态转移方程:根据问题的结构,找到从一个状态转移到另一个状态的方式。
  3. 初始化:为基础情况设置初始值。
  4. 计算顺序:确定计算的顺序,从最小的子问题开始逐步计算到原问题。
  5. 答案提取:从动态规划表中提取最终答案。

动态规划的典型应用场景

  • 最短路径问题(如 Dijkstra、Floyd 算法)。
  • 背包问题(如 0/1 背包、完全背包)。
  • 字符串匹配问题(如最长公共子序列、编辑距离)。
  • 最优子结构问题(如矩阵链乘法问题、切割钢条问题)。

动态规划的分类

动态规划算法通常可以分为以下两种类型:

  1. 自顶向下的递归式动态规划(Top-Down with Memoization):

        使用递归进行问题的求解,并通过记忆化(Memoization)保存已计算的结果,避免重复计算。

  1. 自底向上的迭代式动态规划(Bottom-Up with Tabulation):

        通过迭代从最小子问题开始,逐步解决更大的子问题,直到得到最终结果。

动态规划的三大特点

  1. 最优子结构:问题的最优解可以通过子问题的最优解来构造。例如,最长公共子序列问题的最优解可以通过子问题的解来构造。
  2. 重叠子问题:不同的子问题可能会多次出现,因此可以通过记忆化来存储已解决的子问题,避免重复计算。
  3. 状态转移:通过当前状态和已解决的子问题来推导出下一个状态。

动态规划的经典问题

1. 0/1 背包问题

问题描述:

  • 有一个背包,最多可以容纳重量为 W 的物品。
  • 有 n 个物品,每个物品的重量是 w[i],价值是 v[i]。
  • 问:如何选择物品,使得背包的总价值最大,且总重量不超过 W?

动态规划解法:

  • 定义状态 dp[i][j]:表示前 i 个物品,背包容量为 j 时的最大价值。
  • 状态转移方程:
    • 如果不选择第 i 个物品:dp[i][j] = dp[i-1][j]
    • 如果选择第 i 个物品:dp[i][j] = dp[i-1][j-w[i]] + v[i]
    • 最终的状态转移方程为:dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int knapsack(int W, vector<int>& w, vector<int>& v, int n) {// dp[i][j]表示前i个物品,容量为j时的最大价值vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));for (int i = 1; i <= n; i++) {for (int j = 1; j <= W; j++) {if (w[i - 1] <= j) {// 当前物品不放入或放入两种选择的最大值dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);} else {dp[i][j] = dp[i - 1][j]; // 当前物品不能放入}}}return dp[n][W]; // 返回最大价值
}int main() {int W = 10; // 背包容量vector<int> w = {2, 3, 4, 5}; // 物品重量vector<int> v = {3, 4, 5, 6}; // 物品价值int n = w.size();cout << "Max value in knapsack: " << knapsack(W, w, v, n) << endl;return 0;
}

输出:

Max value in knapsack: 7

2. 最长公共子序列问题

问题描述:

  • 给定两个字符串 s1 和 s2,求它们的最长公共子序列(LCS)。

动态规划解法:

  • 定义状态 dp[i][j]:表示 s1[0...i-1] 和 s2[0...j-1] 的最长公共子序列长度。
  • 状态转移方程:
    • 如果 s1[i-1] == s2[j-1],那么 dp[i][j] = dp[i-1][j-1] + 1
    • 否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1])
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int longestCommonSubsequence(string s1, string s2) {int m = s1.size(), n = s2.size();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 (s1[i - 1] == s2[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]; // 返回最长公共子序列长度
}int main() {string s1 = "abcde";string s2 = "ace";cout << "Length of Longest Common Subsequence: " << longestCommonSubsequence(s1, s2) << endl;return 0;
}

输出:

Length of Longest Common Subsequence: 3

3. 斐波那契数列

斐波那契数列是经典的动态规划问题。其定义是:

  • F(0) = 0
  • F(1) = 1
  • F(n) = F(n-1) + F(n-2)

动态规划解法通过迭代避免了递归中的重复计算。

#include <iostream>
using namespace std;int fib(int n) {if (n <= 1) return n;int prev2 = 0, prev1 = 1;for (int i = 2; i <= n; i++) {int curr = prev1 + prev2;prev2 = prev1;prev1 = curr;}return prev1;
}int main() {int n = 10;cout << "Fibonacci of " << n << " is " << fib(n) << endl;return 0;
}

输出:

Fibonacci of 10 is 55

总结

        动态规划是一种通过将问题分解成小问题并利用已解决的小问题来避免重复计算的技术。其核心思想是使用状态表示问题的不同阶段,并通过状态转移方程来递推求解。在实际应用中,动态规划非常适用于具有“最优子结构”和“重叠子问题”特征的问题,常见的问题有背包问题、最长公共子序列、矩阵链乘法等。


http://www.ppmy.cn/embedded/151745.html

相关文章

气膜滑雪馆:科技创新引领四季滑雪,推动冰雪运动普及—轻空间

随着冬季的到来&#xff0c;冰雪运动迎来了蓬勃发展的时机。然而&#xff0c;冰雪运动一直受到季节和天气的限制&#xff0c;如何让更多人能够跨越这些障碍&#xff0c;全年享受滑雪的乐趣&#xff0c;成为推动冰雪产业普及的关键。自12月以来&#xff0c;“气膜滑雪馆”成为了…

网络爬虫的详细步骤及实现方法

摘要&#xff1a; 本文详细阐述了网络爬虫的主要步骤&#xff0c;包括需求分析、环境搭建、数据采集策略制定、网页解析、数据存储与管理、反爬机制应对以及爬虫的维护与优化等方面。通过对每个步骤的深入探讨&#xff0c;并结合实际代码示例&#xff0c;旨在为读者提供一个全面…

掌握RabbitMQ:全面知识点汇总与实践指南

前言 RabbitMQ 是基于 AMQP 高级消息队列协议的消息队列技术。 特点&#xff1a;它通过发布/订阅模型&#xff0c;实现了服务间的高度解耦。因为消费者不需要确保提供者的存在。 作用&#xff1a;服务间异步通信&#xff1b;顺序消费&#xff1b;定时任务&#xff1b;请求削…

JAVA解析Excel复杂表头

废话不多说&#xff0c;直接上源码。前后端都有哦&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e; 能帮到你记得点赞收藏哦&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#xff5e;&#…

小屏幕下通过css自动实现上下位置颠倒例子

<!DOCTYPE html> <html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>Demo</title><!-- 请勿在项目正式环境中引用该 layui.css 地址 --…

qt qss文件的使用

qt样式的修改方式 一 通过ui界面的改变样式表来直接修改显示效果。 不推荐&#xff0c;其他人不好修改&#xff0c;不够直观&#xff0c;不易维护。 二 通过setStyleSheet接口修改。 一般&#xff0c;界面很少的时候可以使用。一旦界面多起来&#xff0c;代码部分就显得杂乱…

Java中将URL转换为输入流

前言 在Java编程语言中&#xff0c;处理网络资源&#xff08;如图片、文本文件、API响应等&#xff09;是开发过程中常见的需求。为了能够高效地读取这些资源&#xff0c;我们需要将URL转换成InputStream对象&#xff0c;并使用各种输入流操作来处理数据。 使用 URL.openStre…

unity学习11:地图相关的一些基础

目录 1 需要从 unity的 Asset Store 下载资源 1.1 下载资源 1.2 然后可以从 package Manager 里选择下载好的包&#xff0c;import到项目里 2 创建地形 2.1 创建地形 2.2 地形 Terrain大小 2.3 各种网格的尺寸大小 2.4 比较这个地形尺寸和创建的其他物体的大小对比 3 …