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

动态规划原理与LeetCode题解

目录

动态规划的三个特征:

动态规划解题思路:

1.状态转移表法

2. 状态转移方程法

3. LeetCode题解

3.1 LeetCode 509. 斐波那契数

3.2 LeetCode 70. 爬楼梯

3.3 LeetCode 198. 打家劫舍

3.4 LeetCode 53. 最大子序和

3.5 LeetCode 152. 乘积最大子数组

3.6 LeetCode 1014. 最佳观光组合

3.7 LeetCode 55. 跳跃游戏


动态规划适合解决多阶段决策最优解模型问题

动态规划的三个特征:

1.最优子结构

2.无后效性

3.重复子问题

动态规划解题思路:

1.状态转移表法

回溯算法实现-定义状态-画递归树-找重复子问题-画状态转移表-根据递推关系填表

0-1背包问题:我们有一个背包,背包总的承载重量是Wkg。现在我们有n个物品,每个物品的重量不等,并且不可分割。我们现在期望选择几件物品,装载到背包中。在不超过背包所能装载重量的前提下,如何让背包中物品的总重量最大?

我们用一个二维数组states[n][w+1],来记录n个物品放入载重w公斤背包的状态。

第0个(下标从0开始编号)物品的重量是2,要么装入背包,要么不装入背包,决策完之后,会对应背包的两种状态,背包中物品的总重量是0或者2。我们用states[0][0]=true和states[0][2]=true来表示这两种状态。

第1个物品的重量也是2,基于之前的背包状态,在这个物品决策完之后,不同的状态有3个,背包中物品总重量分别是0(0+0),2(0+2 or 2+0),4(2+2)。我们用states[1][0]=true,states[1][2]=true,states[1][4]=true来表示这三种状态。

以此类推,直到考察完所有的物品后,整个states状态数组就都计算好了。我把整个计算的过程画了出来,你可以看看。图中0表示false,1表示true。我们只需要在最后一层,找一个值为true的最接近w(这里是9)的值,就是背包中物品总重量的最大值。

根据上面的状态转移表推导出递推关系,写出动态规划代码

weight:物品重量,n:物品个数,w:背包可承载重量 public int knapsack(int[] weight, int n, int w) { boolean[][] states = new boolean[n][w+1]; // 默认值false states[0][0] = true; // 第一行的数据要特殊处理,可以利用哨兵优化 if (weight[0] <= w) { states[0][weight[0]] = true; } for (int i = 1; i < n; ++i) { // 动态规划状态转移 for (int j = 0; j <= w; ++j) {// 不把第i个物品放入背包 if (states[i-1][j] == true) states[i][j] = states[i-1][j]; } for (int j = 0; j <= w-weight[i]; ++j) {//把第i个物品放入背包 if (states[i-1][j]==true) states[i][j+weight[i]] = true; } } for (int i = w; i >= 0; --i) { // 输出结果 if (states[n-1][i] == true) return i; } return 0; }

2.状态转移方程法

找最优子结构-写状态转移方程-将状态转移方程翻译成代码

3. LeetCode题解

3.1 LeetCode509. 斐波那契数

例如斐波那契数列数列,我们可以很容易知道他的状态转移方程是 f(n)=f(n-1)+f(n-2)。递归的实现如下:

int fib(int n) { if(n<=1) return n; return fib(n-1)+fib(n-2); }

由于计算f(n)需要先计算f(n-1)和f(n-2),但计算f(n-1)又需要计算f(n-2)和f(n-3);计算f(n-2)需要计算f(n-3)和f(n-4)。这个递归推导计算中有大量重复计算,时间复杂度是O(n!)。

重复子问题正是动态规划要解决的重复子问题,很适合用动态规划解决。动态规划实现:

int fib(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]; }

此实现使用了动态规划,问题最优解包含了子问题的最优解(最优子结构),每一步计算都只依赖上一步(无后效性),没有重复计算(解决重复子问题)。这个算法的时间复杂度是O(n),不过空间复杂度也是O(n)。一般动态规划问题都可以写出一个O(n)大小的状态转移方程,不过大部分问题都可以转换为O(1)的空间,因为我们只需要上一步的状态和当前的状态。

int fib(int n) { if(n<=1) return n; int a1 = 0, a2 = 1; int res = 0; for(int i = 2; i <= n; i++) { res = a1+a2; a1 = a2; a2 = res; } return res; }

上面这个算法使用动态规划实现了O(n)的时间复杂度,空间复杂度O(1)。

3.2 LeetCode 70. 爬楼梯

int climbStairs(int n) { if(n<=2) return n; int a1 = 1, a2 = 2; int res = 0; for(int i = 3; i <= n; i++) { res = a1+a2; a1 = a2; a2 = res; } return res; }

归纳总结后发现状态转移方程也是f(n) = f(n-1)+f(n-2),只是要注意初始状态f(1)=1,f(2)=2

3.3 LeetCode 198. 打家劫舍

int rob(vector<int>& nums) { if(nums.size() == 1) return nums[0]; if(nums.size() == 2) return max(nums[0], nums[1]); int a1 = nums[0]; int a2 = max(nums[0], nums[1]); int an = 0; for (int i = 2; i < nums.size(); ++i) { an = max(a1+nums[i],a2); a1 = a2; a2 = an; } return an; }

状态转移方程 f(n) = max(f(n-2)+num[n], f(n-1))

3.4 LeetCode 53. 最大子序和

int maxSubArray(vector<int>& nums) { int maxCur = nums[0], maxRes = nums[0]; for (int i = 1; i < nums.size(); ++i) { maxCur = max(maxCur+nums[i], nums[i]); maxRes = max(maxRes, maxCur); } return maxRes; }

状态转移方程 f(n) = max(f(n-1)+num[i], num[i]);

3.5 LeetCode 152. 乘积最大子数组

int maxProduct(vector<int>& nums) { int maxCur = nums[0]; int minCur = nums[0]; int maxRes = nums[0]; for(int i = 1; i < nums.size(); i++){ int mx = maxCur, mn = minCur; maxCur = max(mx*nums[i], max(nums[i], mn*nums[i])); minCur = min(mn*nums[i], min(nums[i], mx*nums[i])); maxRes = max(maxCur, maxRes); } return maxRes; }

3.6 LeetCode 1014. 最佳观光组合

int maxScoreSightseeingPair(vector<int>& values) { int maxSum = values[0], maxRes = 0; for (int i = 1; i < values.size(); ++i) { maxRes = max(maxRes, maxSum+values[i]-i); maxSum = max(maxSum, values[i]+i); } return maxRes; }

3.7 LeetCode 55. 跳跃游戏

bool canJump(vector<int>& nums) { if(nums.size()==1) return true; int maxIndex{}; for (int i = 0; i < nums.size()-1;i++) { maxIndex = max(maxIndex, i+nums[i]); if(maxIndex < i+1) return false; } return true; }

状态转移方程 f(n) = max(f(n), i+num[i]);

http://www.jsqmd.com/news/1347534/

相关文章:

  • Buzz音频转录工具终极指南:如何在本地高效完成语音转文字
  • 外贸 ERP 软件哪个实用?B2B 外贸与工贸一体选型全指南 - 奔跑123
  • Changedetection.io 技术深度解析:构建企业级网页变更检测系统的架构设计与实践
  • 3分钟掌握纯真IP库:快速查询IP地址的终极解决方案
  • Lano Visualizer:如何将你的桌面变成动态音乐艺术墙?
  • 别急着上Hermes,先把成本、边界和失败兜底算清楚
  • 个人护理穿戴类硅胶用抗菌防霉剂源头厂家哪家靠谱?天诗蓝盾专业解决方案全解析 - 米諾
  • Microsoft OfficeWPS
  • 蒸汽求职怎么样?为什么有人只需短期支持,有人需要长期陪跑
  • 如何快速备份QQ空间:GetQzonehistory完整数据保护终极指南
  • 《SQL 复杂查询优化数据提取 线上高并发排障实战》
  • C++11介绍之enum类型
  • 终极消息防撤回指南:3分钟掌握微信QQ消息永久留存技巧
  • Scalastyle高级技巧:如何编写自定义代码检查器提升团队效率
  • 2026 年压球机厂家推荐观察与测评:煤粉与污泥压球的 4 个踩坑实录 - 新闻快传
  • 保障神经数据隐私与认知主权安全:硬件否决与黑匣子加密双重硬约束的技术治理体系
  • Bilidown终极指南:如何高效下载B站8K超高清视频
  • 2026年沈阳普拉提教练培训机构挑选攻略:威思丽等机构实测梳理 - 自由和远方
  • 甲、乙方必争的“措施费”,你真的清楚吗?
  • 嵌入式软件开发——内存管理原理与应用
  • Stable Diffusion中Seed参数详解:从原理到实战应用
  • 如何高效下载M3U8视频:5个实用技巧与完整解决方案
  • 终极指南:如何快速掌握Arduino ESP32物联网开发
  • 3分钟搞定Arduino ESP32开发:物联网与嵌入式系统终极入门指南
  • 雷达基本方程:从原理到工程实践,掌握雷达探测性能的量化核心
  • 第一份工作的起点,会怎样影响之后的职业选择?
  • 甘肃电源储能系统研发哪家好?从技术方案、应用场景、成本测算到西北极端环境适配,看懂真实交付边界 - 中国华商产业观察网
  • 播客听完了跟没听一样?通义听悟、Ai好记、BiBiGPT、百度网盘AI四款播客转笔记工具横评
  • CC Switch深度链接:AI配置一键导入的终极指南
  • JAYA算法优化随机森林回归:突破传统调参瓶颈的智能参数搜索方案