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

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

王子,公主请阅🚀

要开心

要快乐

顺便进步

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

【Java Web学习 | 第1篇】前端 - HTML

【Java Web学习 | 第1篇】前端 - HTML

文章目录 * Java Web概览 * HTML核心知识点总结 * 一、HTML基础概念🥝 * 1.1 HTML文档基本结构 * 1.2 HTML标签特点 * 二、常用HTML标签🧾 * 2.1 文本标签 * 2.2 链接与图像 * 综合示例 * 2.3 列表标签 * 2.4 表格标签 * 2.5 表单标签 * 三、HTML5新增特性🤔 * 3.1 语义化标签 * 3.2 媒体标签 * 3.3 其他新增特性 * 四、学习资源推荐🐦‍🔥 Java Web概览 HTML核心知识点总结 一、HTML基础概念🥝 1.1

By Ne0inhk

Qwen3-VL-WEBUI技术整合:与RAG系统协同工作教程

Qwen3-VL-WEBUI技术整合:与RAG系统协同工作教程 1. 引言 随着多模态大模型的快速发展,视觉-语言理解能力已成为AI应用落地的关键一环。阿里云推出的 Qwen3-VL 系列模型,作为迄今为止Qwen系列中最强大的视觉-语言模型(Vision-Language Model, VLM),在文本生成、图像理解、视频分析和代理交互等方面实现了全面升级。 本文将聚焦于开源项目 Qwen3-VL-WEBUI ——一个专为Qwen3-VL设计的本地化Web交互界面,并深入讲解如何将其与 RAG(Retrieval-Augmented Generation)系统 进行深度整合,构建具备“看图说话+知识检索+智能推理”三位一体能力的下一代AI应用。 该WEBUI内置了 Qwen3-VL-4B-Instruct 模型,支持图像上传、对话交互、工具调用等核心功能,开箱即用,适合快速原型开发与工程集成。 通过本教程,你将掌握: - 如何部署并运行 Qwen3-VL-WEBUI - RAG系统的基本架构与选型建议 - 实现图文混合输入下的动态知识增强机制 - 构建可扩展的多模态问答系

By Ne0inhk
Python Web 开发进阶实战:AI 原生安全防护 —— 在 Flask + Suricata 中构建智能网络威胁狩猎平台

Python Web 开发进阶实战:AI 原生安全防护 —— 在 Flask + Suricata 中构建智能网络威胁狩猎平台

第一章:从规则防御到行为智能 1.1 传统安全的局限 技术缺陷 签名检测(Snort/Suricata) | 仅能识别已知攻击模式防火墙 ACL | 无法阻止合法端口上的恶意流量SIEM 告警 | 海量日志 → 分析瘫痪 1.2 AI 安全的优势 * 无监督学习:无需标注攻击样本 * 上下文感知:结合用户角色、历史行为 * 预测性:在破坏发生前预警 案例:某企业通过 DNS 请求熵值异常,提前 14 天发现 Cobalt Strike C2。 第二章:平台架构设计 2.1 数据流全景 [网络流量] │ ├── [Suricata] → 实时 IDS 告警(JSON) ├── [Zeek] → 连接日志(conn.

By Ne0inhk
突破亚马逊壁垒,Web Unlocker API 助您轻松获取数据

突破亚马逊壁垒,Web Unlocker API 助您轻松获取数据

目录 * 一、Web Unlocker API简介 * 二、开始使用Web Unlocker API * 1、首先进入控制台页面,点击左侧第一个tab键“代理 & 抓取基础设施”,找到“网页解锁器”,开始使用。 * 2、进入网页解锁器页面后,填写通道名称,添加简短描述,点击添加 * 3、直接展示代理基础设施/web_unlocker3的详细信息 * 4、配置网页解锁器 * 5、以Python脚本获取亚马逊平台数据为示例 * 6、结果示例 * 三、Web Scraper * 1、快速使用Web Scraper * 2、通过python获取亚马逊网页数据 * 3、定位具体数据 * 4、运行并保存到csv文件 * 四、SERP API * 五、优惠升级

By Ne0inhk