【动态规划】陶然无喜亦无忧,人生且自由 - 简单多状态模型

【动态规划】陶然无喜亦无忧,人生且自由 - 简单多状态模型
本篇博客给大家带来的是简单多状态之动态规划解法技巧.
🐎文章专栏: 动态规划
🚀若有问题 评论区见
❤ 欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅🚀

要开心

要快乐

顺便进步

1. 按摩师

题目链接:面试题 17.16. 按摩师

题目内容:

一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

注意:本题相对原题稍作改动

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
示例 2:

输入: [2,7,9,3,1]
输出: 12
解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
示例 3:

输入: [2,1,4,5,3,1,1,3]
输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。

第一 步骤分析

1. 状态表示
dp[i]表示选择到 i 位置时得最长预约时间. 选择到 i 位置时, 可由 i 位置是否选择分两种情况:
f[i] 表示选择到 i 位置时 nums[i] 必选的最长时间.
g[i] 表示选择到 i 位置时 nums[i] 不选的最长时间.

2. 状态转移方程

先分析关于f[i]的递推公式, 如上图, nums[i] 必选意味着nums[i-1]必定不选. 从起始点到[i-1]且nums[i-1]不选, 不就是g[i-1]吗?
所以f[i] = g[i-1] + nums[i];

再求g[i] 的递推公式
如上图, nums[i]不选, nums[i-1] 可选可不选.
nums[i-1] 选择时, g[i] = f[i-1]
nums[i-1] 不选时, g[i] = g[i-1]
又因为是求最大值,所以g[i] = Math.max(f[i-1],g[i-1]);

3. 初始化
f[0] = nums[0]

4. 填表顺序
从左往右填表

5. 返回值
返回Math.max(f[n-1],g[n-1]);

第二 代码实现
classSolution{publicintmassage(int[] nums){//1. 创建dp表int n = nums.length;int[] f =newint[n];int[] g =newint[n];//2. 初始化if(nums.length ==0)return0; f[0]= nums[0];//3. 填表for(int i =1;i < n;++i){ f[i]= g[i-1]+ nums[i]; g[i]=Math.max(f[i-1],g[i-1]);}returnMath.max(f[n-1],g[n-1]);}}

2. 打家劫舍

题目链接:213. 打家劫舍 II

题目内容:

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:

输入:nums = [1,2,3]
输出:3

提示:

1 <= nums.length <= 100
0 <= nums[i] <= 1000

第一 整体分析

由题意知, 偷了第一个位置就不能偷最后一个位置, 第一个位置的情况会影响动态规划的五个步骤, 于是需要分两种情况去讨论, 两种基本的步骤与上道题一致, 初始化和填表会有不同.

第二 动态规划

1. 状态表示
dp[i]表示偷到 i 位置的最高金额. 根据 i 位置的 偷 与 不偷 分两种状态:
f[i] 表示 到 i 位置 nums[i]也偷的最高金额
g[i] 表示 到 i 位置 nums[i] 不偷的最高金额

2. 状态转移方程
跟上道题一样的分析思路,不多说明,直接给出结果:
f[i] = g[i] + nums[i];
g[i] = Math.max(f[i-1],g[i-1]);

3. 初始化
f[0] = nums[0]

4. 填表顺序
从左到右同时填表

5. 返回值
返回Math.max(x,y)

第三 代码实现
classSolution{publicintrob(int[] nums){//1.创建dp表int n = nums.length;int[] f =newint[n];int[] g =newint[n];int[] f1 =newint[n];int[] g1 =newint[n];//2.初始化if(n ==1)return nums[0];//第一个位置偷的初始化 f[0]= nums[0]; g[1]= nums[0];//第一个位置不偷的初始化 f1[1]= nums[1];//3.填表for(int i =2;i < n-1;++i){ f[i]= g[i-1]+ nums[i]; g[i]=Math.max(f[i-1],g[i-1]);}int x =Math.max(f[n-2],g[n-2]);for(int i =2;i < n;++i){ f1[i]= g1[i-1]+ nums[i]; g1[i]=Math.max(f1[i-1],g1[i-1]);}int y =Math.max(f1[n-1],g1[n-1]);returnMath.max(x,y);}}

3. 删除并获得点数

题目链接:740. 删除并获得点数

题目内容:

给你一个整数数组 nums ,你可以对它进行一些操作。

每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。

开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

示例 1:

输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。
示例 2:

输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。

提示:

1 <= nums.length <= 2 * 104
1 <= nums[i] <= 104

第一 预处理

直接上动态规划会发现选择完nums[i], 很难处理nums[i-1]和nums[i+1].所以先预处理nums数组.
创建一个arr数组, arr[i] 是 nums[i] 中 i 元素的总和, 比如arr[2] = 2+2 = 4
这样一来, 只要选了arr[i], 相邻位置就不能选了. 这其实就跟第一题"按摩师"一样了.

第二 动态规划

1. 状态表示
dp[i] 表示选到 i 位置时的最大点数, 又根据arr[i] 是否选择分为两种情况:
f[i] 表示到达 i 位置arr[i] 要选的最大点数
g[i] 表示到达 i 位置arr[i] 不选的最大点数

2. 状态转移方程
分析过程与第一题一样,便直接给出结果
f[i] = g[i-1] + arr[i];
g[i] = Math.max(g[i-1],f[i-1]);

3. 初始化
f[0] = arr[0];
g[0] = 0;

4. 填表顺序
从左往右同时填

5. 返回值
返回Math.max(f[n-1],g[n-1]);

第三 代码实现
classSolution{publicintdeleteAndEarn(int[] nums){//预处理int n =10001;int[] arr =newint[n];for(int x : nums) arr[x]+= x;//创建dp表int[] f =newint[n];int[] g =newint[n];//初始化 f[0]= arr[0];//填表for(int i =1;i < n;++i){ f[i]= g[i-1]+ arr[i]; g[i]=Math.max(g[i-1],f[i-1]);}returnMath.max(f[n-1],g[n-1]);}}

4. 粉刷房子

题目链接:LCR 091. 粉刷房子

题目内容:

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。

当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。

例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。

请计算出粉刷完所有房子最少的花费成本。

示例 1:

输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。
最少花费: 2 + 5 + 3 = 10。
示例 2:

输入: costs = [[7,6,2]]
输出: 2

提示:

costs.length == n
costs[i].length == 3
1 <= n <= 100
1 <= costs[i][j] <= 20

第一 步骤分析

1. 状态表示
dp[i] 表示到达 i 位置粉刷完所有房子的最小花费. 根据 i 位置粉刷的颜色可以继续细分:
dp[i][0] 表示到达 i 位置, i 粉刷红色的最小花费
dp[i][1] 表示到达 i 位置, i 粉刷蓝色的最小花费
dp[i][2] 表示到达 i 位置, i 粉刷绿色的最小花费

2. 状态转移方程
先分析dp[i][0],
当i-1位置是蓝色时, dp[i][0] = dp[i-1][1] + costs[i][0];
当i-1位置是红色时, dp[i][0] = dp[i-1][2] + costs[i][0];
又因为要求最小花费, 所以:
dp[i][0] = Math.min(dp[i-1][1],dp[i-1][2]) + costs[i][0];
同理dp[i][1] = Math.min(dp[i-1][0],dp[i-1][2]) + costs[i][1];
dp[i][2] = Math.min(dp[i-1][0],dp[i-1][0]) + costs[i][2];

3. 初始化
①: 不创建虚拟节点, 就直接dp[0][0] = costs[0][0];
dp[0][1] = costs[0][1];
dp[0][2] = costs[0][2];
②: 创建虚拟节点需要处理两个细节
第一 dp表与原数组之间的下标对应关系是: dp[i][0] -> costs[i-1][0]

第二 虚拟节点的初始化, 为了保证填表的正确, 比如填写dp[i][0]表,
为了保证dp[1][0] 必须为 costs[0][0], 看递推公式dp[i][0],所以dp[0][1] = 0,dp[0][2] = 0;
同理 dp[0][0] = 0

4. 填表顺序
从左往右填写三个表.

5. 返回值
有虚拟节点时, Math.min(dp[n][0],Math.min(dp[n][1],dp[n][2]));
无虚拟节点时, 把 n 改成 n-1即可.

第二 代码实现
classSolution{publicintminCost(int[][] costs){// //创建dp表(不带虚拟节点)// int n = costs.length;// int[][] dp = new int[n][3];// //初始化dp表// //dp[0][0],dp[0][1],dp[0][2]默认值即可// dp[0][0] = costs[0][0];dp[0][1] = costs[0][1];dp[0][2] = costs[0][2];// //填表// for(int i = 1;i < n;++i) {// dp[i][0] = Math.min(dp[i-1][1],dp[i-1][2]) + costs[i][0];//注意原数组与dp表下标映射关系.// dp[i][1] = Math.min(dp[i-1][0],dp[i-1][2]) + costs[i][1];// dp[i][2] = Math.min(dp[i-1][0],dp[i-1][1]) + costs[i][2];// }// return Math.min(dp[n-1][0],Math.min(dp[n-1][1],dp[n-1][2]));//创建dp表(带虚拟节点)int n = costs.length;int[][] dp =newint[n+1][3];//初始化dp表//dp[0][0],dp[0][1],dp[0][2]默认值即可//填表for(int i =1;i <= n;++i){ dp[i][0]=Math.min(dp[i-1][1],dp[i-1][2])+ costs[i-1][0];//注意原数组与dp表下标映射关系. dp[i][1]=Math.min(dp[i-1][0],dp[i-1][2])+ costs[i-1][1]; dp[i][2]=Math.min(dp[i-1][0],dp[i-1][1])+ costs[i-1][2];}returnMath.min(dp[n][0],Math.min(dp[n][1],dp[n][2]));}}

本篇博客到这里就结束啦, 感谢观看 ❤❤❤
🐎期待与你的下一次相遇😊😊😊

Read more

带可二次开发的管理配置端 + 非低代码 + 原生支持标准化 Skill框架选择

「带可二次开发的管理配置端 + 非低代码 + 原生支持标准化 Skill」的开源 Agent 框架,筛选 3款完全匹配的框架(均为代码级可扩展、自带 Skill 管理后台、支持 SKILL.md/MCP 标准),附核心特性、二次开发要点和部署步骤,都是企业级/开发者友好的选型: 一、首选:LangGraph + LangServe(LangChain 官方生态,Python 栈,极致可扩展) 核心定位 LangChain 官方推出的「Agent 编排 + 服务化」框架,自带可二次开发的 Skill/Tool 管理后台(LangServe Dashboard),纯代码开发、无低代码封装,是 Python 生态的最佳选择。 关键特性

By Ne0inhk
《星辰 RPA 全自动:做一个小红书自动发文机器人》

《星辰 RPA 全自动:做一个小红书自动发文机器人》

前引:在企业数智化转型的浪潮中,如何突破 “有 AI 无落地、有流程无智能” 的困局?星辰 Agent 与星辰 RPA 的出现,正是为了解决这一痛点。作为科大讯飞旗下的双核心产品,星辰 Agent 以企业级 Agentic Workflow 开发平台为底座,提供 AI 工作流编排、模型管理与跨系统连接能力;而星辰 RPA 则以超过 300 个自动化原子能力,让业务流程真正 “动” 起来! 目录 一、企业机器人自动化平台:RPA (1)RPA介绍 (2)服务端安装 (1)clone项目 (2)配置为本地访问 (3)检查镜像源 (4)配置default.conf

By Ne0inhk
智元机器人三大产线

智元机器人三大产线

执行摘要 2025 年 12 月 8 日,智元机器人迎来了具有里程碑意义的时刻 —— 第 5000 台通用具身机器人在上海临港工厂正式量产下线。这一成就标志着中国具身智能产业从技术验证阶段全面迈入规模商用时代。智元机器人通过三年的快速发展,已建立起远征、灵犀、精灵三大产品矩阵,累计出货 5000 台,其中远征 A1/A2 下线 1742 台,灵犀 X1/X2 下线 1846 台,精灵 G1/G2 下线 1412 台(3)。 在技术层面,智元机器人实现了多项重大突破。其自主研发的 PowerFlow 关节电机峰值扭矩超过 350N・m,重量仅 1.6kg,采用准直驱技术方案,相较传统谐波减速器方案成本降低

By Ne0inhk

无人机飞行空域申请全流程指南

无人机飞行空域申请全流程指南 一、哪些情况需要申请空域? 必须申请空域的情况: * 在管制空域内飞行(包括机场周边、军事区、120米以上空域等) * 微型/轻型无人机在适飞空域内超过真高120米飞行 * 轻型无人机进行特殊操作(如中继飞行、载运危险品、飞越人群) * 小型及以上无人机(空机>4kg或最大起飞重量>7kg)在任何空域飞行 无需申请的情况: * 微型无人机在真高50米以下适飞空域内飞行 * 轻型无人机在真高120米以下适飞空域内飞行 二、申请前必备准备 1️⃣ 实名登记(所有无人机必备) * 登录民用无人驾驶航空器综合管理平台(UOM)(https://uom.caac.gov.cn或UOM APP) * 个人用户:完成实名认证(上传身份证),为≥250g的无人机登记,获取唯一编码和二维码 * 企业用户:准备营业执照、法人身份证、运营合格证、无人机适航证 2️⃣ 人员资质要求

By Ne0inhk