打家劫舍与最长有效括号:动态规划与字符串处理的双重魅力

article/2025/6/28 5:58:19

博客引言:

在我们的生活中,算法无处不在,它不仅帮助我们解决复杂的问题,还能揭示隐藏在数据背后的规律。今天,我们将通过两个有趣的问题,探索算法在动态规划与字符串处理中的智慧。

首先,我们将探讨打家劫舍问题,看看如何通过动态规划找到偷窃的最大金额。接着,我们将分析最长有效括号问题,探讨如何通过栈结构高效找到最长的有效括号子串。通过这两个案例,你将看到算法如何在实际问题中发挥作用,帮助我们找到最优解。

让我们一起进入算法的世界,探索这些隐藏在动态规划与字符串处理背后的智慧!

博客正文:

第一章:神偷的夜行秘籍——《打家劫舍》

🎭 剧情设定:你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,但相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算在不触动警报的情况下,一夜之内能够偷窃到的最高金额。

算法核心:动态规划
这个问题可以通过动态规划来解决。动态规划是一种通过将问题分解为更小的子问题来解决复杂问题的方法。具体来说,我们可以定义一个状态数组,记录到第i个房屋时的最大偷窃金额。

🔍 核心矛盾

  • 贪婪陷阱:若只选金额最大的房屋,可能因相邻限制错失全局最优。

  • 连锁反应:每个选择影响后续所有可能性(动态规划的典型特征)。

⚙️ 动态规划的精髓解析

  1. 状态定义:定义一个数组 dp,其中 dp[i] 表示到第i个房屋时的最大偷窃金额。
  2. 状态转移:对于每个房屋i,有两种选择:
    • 偷窃第i个房屋:那么不能偷窃第i-1个房屋,最大金额为 dp[i-2] + nums[i]
    • 不偷窃第i个房屋:那么最大金额为 dp[i-1]。 因此,状态转移方程为:
dp[i] = max( dp[i-1], dp[i-2] + nums[i] )
  1. 初始化dp[0] = nums[0]dp[1] = max(nums[0], nums[1])

  • dp[i-1]:不偷第 i 间,继承前 i-1 间的最优解。

  • dp[i-2] + nums[i]:偷第 i 间,跳过 i-1 间,取前 i-2 间的最优解加当前收益。

🌰 实例推演(输入 [2,7,9,3,1])

  1. 初始化dp[0]=2(偷第1间),dp[1]=max(2,7)=7(第1、2间选更大者)。

  2. 状态转移

    • dp[2] = max(dp[1], dp[0]+9) = max(7, 2+9)=11

    • dp[3] = max(dp[2], dp[1]+3) = max(11, 7+3)=11

    • dp[4] = max(dp[3], dp[2]+1) = max(11, 11+1)=12

  3. 终局:输出 12,对应偷第1、3、5间。

💡 算法哲学:每个局部最优解都是全局最优解的基石,拒绝短视贪婪,方成“盗亦有道”。

#include <stdio.h>   // 引入标准输入输出库,用于printf和scanf等函数
#include <stdlib.h>  // 引入标准库,用于内存分配malloc和free等函数// 函数:计算可偷窃的最大金额
// 参数:nums - 整型数组指针,表示每个房屋的金额
//       numsSize - 数组长度
// 返回值:可偷窃的最大金额
int rob(int* nums, int numsSize) {// 处理特殊情况:空数组if (numsSize == 0) return 0;  // 如果没有房屋,返回0// 处理特殊情况:只有一个房屋if (numsSize == 1) return nums[0];  // 如果只有一个房屋,直接返回其金额int prevMax = nums[0];  // 初始化前前个房屋的最大收益(即第0个房屋的收益)// 初始化前个房屋的最大收益(比较第0个和第1个房屋的收益)int currentMax = (nums[0] > nums[1]) ? nums[0] : nums[1];// 从第三个房屋开始动态规划(索引2开始)for (int i = 2; i < numsSize; i++) {int temp = currentMax;  // 临时保存当前最大值(用于后续更新prevMax)// 状态转移方程:// 比较两种选择:偷当前房屋(prevMax + nums[i])或不偷(currentMax)currentMax = (currentMax > prevMax + nums[i]) ? currentMax : prevMax + nums[i];prevMax = temp;  // 更新前前个房屋的最大收益为前次计算的currentMax}return currentMax;  // 返回最终计算的最大金额
}int main() {int n;  // 定义变量:房屋数量printf("请输入房屋数量:");  // 提示用户输入scanf("%d", &n);  // 读取用户输入的房屋数量// 输入验证:确保房屋数量是正整数if (n <= 0) {printf("错误:房屋数量必须为正整数!\n");  // 错误提示return 1;  // 非正常退出}// 动态分配内存:创建存储房屋金额的数组// 分配 n 个整型大小的内存空间int* nums = (int*)malloc(n * sizeof(int));// 检查内存分配是否成功if (nums == NULL) {printf("内存分配失败!\n");  // 内存分配失败提示return 1;  // 非正常退出}// 提示用户输入房屋金额printf("请输入%d个房屋的金额(用空格分隔):", n);// 循环读取用户输入的每个房屋金额for (int i = 0; i < n; i++) {scanf("%d", &nums[i]);  // 将输入值存储到数组相应位置}// 调用rob函数计算最大可偷窃金额int result = rob(nums, n);// 输出计算结果printf("最大可偷窃金额为:%d\n", result);free(nums);  // 释放动态分配的内存,防止内存泄漏return 0;  // 正常退出程序
}

输出结果:


第二章:括号猎人的密码战——《最长有效括号》

🎭 剧情设定
你面对一串混乱的括号序列 (()))()),需找出最长连续有效子串(如 ()() 有效, ()) 无效)。

算法核心:栈结构与括号匹配
这个问题可以通过栈结构来解决。栈结构可以帮助我们记录有效括号的位置,从而计算最长的有效长度。

详细分析:

  1. 初始化栈:栈用于记录括号的位置,初始时栈中放入一个-1作为基准。
  2. 遍历字符串:对于每个字符,如果是 '(',则将其索引压入栈中;如果是 ')',则弹出栈顶元素。如果栈为空,将当前索引压入栈中作为新的基准;否则,计算当前索引与栈顶元素的差值,更新最长有效长度。
  3. 返回结果:遍历完成后,最长有效长度即为所求。

🔍 核心矛盾

  • 连续性要求:有效括号必须连续(如 ()(()) 的最长有效子串是后4位 (()))。

  • 嵌套干扰:左括号 ( 可能被后续 ) 匹配,也可能因位置错误失效。

⚙️ 动态规划的进阶技巧
定义 dp[i] 为以第 i 个字符结尾的最长有效括号长度:

  • Case 1:s[i] = '('
    直接置 dp[i]=0(有效括号必以 ) 结尾)。

  • Case 2:s[i] = ')'`

    1. 前一位是 '(':形如 ...() ,则 dp[i] = dp[i-2] + 2

    2. 前一位是 ')':形如 ...)) ,则需检查 s[i - dp[i-1] - 1] 是否为 (

      • 若是,则 dp[i] = dp[i-1] + 2 + dp[i - dp[i-1] - 2](跨过已匹配的子串)。

🌰 实例推演(输入 ")()())")

  1. 初始化:dp = [0,0,0,0,0,0]

  2. 逐步计算:

    • i=1s[1]='(' → dp[1]=0

    • i=2s[2]=')' 且 s[1]='(' → dp[2]=dp[0]+2=2

    • i=3s[3]='(' → dp[3]=0

    • i=4s[4]=')' 且 s[3]='(' → dp[4]=dp[2]+2=4

    • i=5s[5]=')',前一位 s[4]=')',检查 s[5 - dp[4] - 1] = s[0]=')' → 无效,dp[5]=0

  3. 终局:最大值为 4(子串 ()())。

💡 算法哲学:括号匹配是“时空对称”的艺术,动态规划通过保存历史状态,破解嵌套中的时空连续性。

#include <stdio.h>   // 引入标准输入输出库,用于printf和fgets等函数
#include <stdlib.h>  // 引入标准库,用于内存分配malloc和free等函数
#include <string.h>  // 引入字符串处理库,用于strlen和strcspn等函数// 函数:计算最长有效括号子串长度
// 参数:s - 字符指针,指向包含括号的字符串
// 返回值:最长有效括号子串的长度
int longestValidParentheses(char* s) {int n = strlen(s);  // 计算输入字符串的长度if (n <= 1) return 0; // 长度小于2不可能有有效括号,直接返回0// 创建动态规划数组 dp[i]表示以s[i]结尾的最长有效括号长度int* dp = (int*)malloc(n * sizeof(int));  // 动态分配n个整数大小的内存空间if (!dp) {  // 检查内存分配是否成功printf("内存分配失败!\n");  // 内存分配失败提示exit(1);  // 退出程序}// 初始化dp数组所有元素为0memset(dp, 0, n * sizeof(int));  // 使用memset将整个数组初始化为0int maxLen = 0; // 记录最大长度的变量,初始化为0// 动态规划计算:从索引1开始遍历字符串for (int i = 1; i < n; i++) {if (s[i] == ')') {  // 当前字符是右括号时才可能形成有效括号对// 情况1: 前一个字符是 '(',形如 "...()"if (s[i - 1] == '(') {  // 检查前一个字符是否是左括号dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;  // 如果i>=2,加上前前位置的dp值再加2}// 情况2: 前一个字符是 ')',形如 "...))"else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') {  // 检查是否形成嵌套结构// 获取匹配左括号之前的有效长度(如果存在)int prev = (i - dp[i - 1] - 2 >= 0) ? dp[i - dp[i - 1] - 2] : 0;dp[i] = dp[i - 1] + 2 + prev;  // 计算当前有效长度}// 更新最大长度if (dp[i] > maxLen) maxLen = dp[i];  // 如果当前值更大则更新maxLen}// s[i] == '(' 的情况,dp[i]保持0(因为以左括号结尾不可能是有效括号串的结尾)}free(dp); // 释放动态分配的内存,防止内存泄漏return maxLen;  // 返回找到的最大长度
}int main() {char input[1000]; // 输入缓冲区,最大999字符+1个结束符printf("请输入括号序列(只包含 '(' 和 ')'):");  // 提示用户输入if (fgets(input, sizeof(input), stdin) == NULL) {  // 读取一行输入printf("输入错误!\n");  // 输入失败提示return 1;  // 非正常退出}// 移除可能的换行符:查找换行符位置并替换为字符串结束符input[strcspn(input, "\n")] = '\0';  // strcspn找到第一个换行符的位置// 验证输入是否只包含括号for (int i = 0; input[i] != '\0'; i++) {  // 遍历输入字符串if (input[i] != '(' && input[i] != ')') {  // 检查每个字符是否是括号printf("错误:输入只能包含 '(' 和 ')' 字符!\n");  // 错误提示return 1;  // 非正常退出}}int result = longestValidParentheses(input);  // 调用计算函数printf("最长有效括号子串长度:%d\n", result);  // 输出结果return 0;  // 正常退出程序
}

输出结果:


第三章:双子星对决——动态规划的阴阳辩证

📊 双题对比分析表

维度打家劫舍最长有效括号
问题类型最优化问题最长连续子串问题
状态定义dp[i]: 前 i 间房的最大收益dp[i]: 以 s[i] 结尾的有效长度
转移方程核心当前偷或不偷(二选一)当前字符与历史字符的匹配关系
空间复杂度O(1)(滚动变量优化)O(n)
难点突破避免相邻选择处理嵌套结构(如 (())
哲学隐喻贪心与克制的平衡对称与连续的辩证统一

🔄 思维模型对比图

打家劫舍:线性决策链  
[房屋1] → [房屋2] → [房屋3]  
   │        │        │  
  偷/弃    偷/弃    偷/弃  

最长有效括号:时空回溯匹配  
... [  (  [已匹配子串]  )  ]  
      ↑        ↑        ↑  
    检查点  历史状态  当前字符

🌟 核心总结

  1. 打家劫舍:决策是线性的,每个状态仅依赖前两个状态,体现“步步为营”的稳健。

  2. 最长有效括号:决策是跳跃的,需回溯到历史子串起点,体现“时空折叠”的巧妙。

  3. 动态规划的灵魂

    • 状态定义是破局的关键(收益 vs 长度)。

    • 转移方程是逻辑的结晶(二选一 vs 匹配检查)。

    • 边界处理是稳健的基石(空数组、单字符等)。


博客结语

动态规划,是算法世界的“预言之书”。它教会我们:

小偷的智慧——真正的赢家不是贪得无厌者,而是懂得权衡取舍的谋士;
括号的密码——最复杂的迷宫,终将被对称的逻辑与历史的记忆破解。

无论你是算法新手还是江湖老手,这两个问题都将重塑你对“最优解”的认知。下次面对难题时,不妨自问:

“若是小偷,如何取舍?若是括号,何处匹配?”


📣 互动话题

如果给小偷增加“每隔两房必偷一次”的限制,如何修改状态方程?

括号问题能否用栈代替DP?各有何优劣?


http://www.hkcw.cn/article/UhwEkrxOqL.shtml

相关文章

Ⅲ-1.计算机二级选择题(三大结构之基本语句)

【注&#xff1a;重点题以及添加目录格式导航&#xff01;&#xff01;&#xff01;】 【重点题】&#xff08;第1题&#xff09; 【重点题】&#xff08;第5题&#xff09; 【重点题】&#xff08;第7题&#xff09; 【重点题】&#xff08;第11题&#xff09; 【重点题】&…

demo_win10配置WSL、DockerDesktop环境,本地部署Dify,ngrok公网测试

win10配置WSL、DockerDesktop环境&#xff0c;本地部署Dify&#xff0c;ngrok分享测试 一、配置WSL 1.1 开启Hyper-V 安装WSL2首先要保证操作系统可以开启hyper-v功能&#xff0c;默认支持开启hyper-v的版本为&#xff1a;Windows11企业版、专业版或教育版,而家庭版是不支持…

【仿生机器人】刀剑神域计划——仿生机器人.亚丝娜

我在做仿生机器人头&#xff0c;硬件部分已经搭建完毕&#xff0c;包括头部和颈部&#xff0c;用的23个舵机驱动机器人做表情&#xff0c;也支持头部的旋转&#xff08;就是颈部的功能&#xff09;&#xff0c;安装了摄像头在眼睛中&#xff0c;还有麦克风接受周围环境声音&…

【小沐杂货铺】基于Three.JS构建IFC模型浏览器(WebGL、CAD、Revit、IFC)

文章目录 1、简介1.1 Three.JS1.1 IFC.JS 2、示例代码2.1 示例12.2 示例22.3 示例32.4 示例42.5 示例52.6 示例62.7 示例72.8 示例82.9 示例92.10 示例10 结语 1、简介 1.1 Three.JS https://threejs.org/ Three.js 是一个基于 WebGL 的 JavaScript 3D 库&#xff0c;它封装了…

Spark-TTS: AI语音合成的“变声大师“

嘿&#xff0c;各位AI爱好者&#xff01;还记得那些机器人般毫无感情的合成语音吗&#xff1f;或者那些只能完全模仿但无法创造的语音克隆&#xff1f;今天我要介绍的Spark-TTS模型&#xff0c;可能会让这些问题成为历史。想象一下&#xff0c;你可以让AI不仅说出任何文字&…

【AI论文】表R1:表格推理的推理时间扩展

摘要&#xff1a;在这项工作中&#xff0c;我们提出了第一个研究&#xff0c;探索推理时间缩放对表格推理任务的影响。 我们开发和评估了两种训练后策略来实现推理时间扩展&#xff1a;前沿模型推理轨迹的蒸馏和具有可验证奖励的强化学习&#xff08;RLVR&#xff09;。 对于蒸…

学习STC51单片机25(芯片为STC89C52RCRC)

每日一言 生活就像弹簧&#xff0c;你弱它就强&#xff0c;你强它就弱&#xff0c;别轻易认输。 ESP8266作为路由器模式&#xff08;AP模式&#xff09;也就是在局域网内可以有服务器的作用 那么我们需要将pc作为设备进行连接ESP的发射出来的WIFE 叫做这个AI啥的 也有可能叫做…

基于Android的拼车系统的设计与实现

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

美媒发现:中国持续上升,美国跌成负值

美媒公布最新民调:全球对中国好感度上升,美国形象转而跌入负区间值美国Axios新闻网2日援引晨间咨询公司(Morning Consult)最新民调数据称,全球对中国的好感度持续上升,而对美国的好感度则跌入负区间值,美国贸易政策正以牺牲其自身利益为代价,助推中国崛起。Axios新闻网…

马克龙笑容满面邀妻子与球队合影 甜蜜互动成焦点

马克龙笑容满面邀妻子与球队合影 甜蜜互动成焦点。5月28日,法国总统马克龙结束了对越南的访问后,与妻子布里吉特一同抵达印度尼西亚,开启正式访问行程。在越南访问期间,一段布里吉特“打脸”马克龙的视频引发了热议,尽管马克龙解释这只是两人间的玩笑,但这一事件仍让他显…

FFmpeg移植教程(linux平台)

目录 第三方源码编译三部曲关于 configure 的说明 FFmpeg 移植流程获取源码方法一&#xff1a;git 远程克隆方法二&#xff1a;官网下载压缩包解压 配置安装 第三方源码编译三部曲 Linux平台下有许多开源的第三方库和服务&#xff0c;这些开源代码一般都符合GNU-autotools编码…

ERP管理系统:Java+Vue,含源码及文档,涵盖采购、销售、库存等业务,优化企业运营

前言&#xff1a; 在当今竞争激烈的商业环境中&#xff0c;企业需要高效、精准地管理各个业务环节&#xff0c;以提升运营效率、降低成本、增强市场竞争力。ERP管理系统作为一种集成化的管理工具&#xff0c;将企业的各个核心业务模块整合在一个统一的平台上&#xff0c;实现了…

shiro使用详解

01-Shiro 实战教程 1.权限的管理 1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现 对用户访问系统的控制 &#xff0c;按照安全规则或者 安全策略 控制用户可以访问而且只能访问自己被授权的资…

ACTF2025-web-eznote-wp

附件审计 app.js const express require(express) const session require(express-session) // 会话管理中间件 const { randomBytes } require(crypto) // 生成加密随机数 const fs require(fs) // 文件系统操作 const spawn require(child_process) // 执行外部命令&a…

CSS 3D 变换中z-index失效问题

CSS 3D 变换中 z-index 失效问题 1. z-index 失效了 在 CSS 中&#xff0c;z-index 通常用于控制元素的层叠顺序&#xff0c;数值越大&#xff0c;元素越靠前显示。在 3D 变换&#xff08;如 rotateX、translateZ&#xff09; 中使用 z-index 时&#xff0c;可能会发现z-inde…

能源行业的网络安全:一场无声的战争

想象一下&#xff0c;你家的电力突然中断&#xff0c;冰箱里的食物开始变质&#xff0c;空调停止运转&#xff0c;甚至连手机充电都成了奢望。这不是科幻电影&#xff0c;而是网络攻击可能给我们的生活带来的真实影响。能源行业&#xff0c;这个维系现代社会运转的命脉&#xf…

ESP32-C3 + W5500 + MicroPython 编译记录

前言 我本来是想连个网&#xff0c;结果连上了无数个坑…… 在这个项目中&#xff0c;我的目标是用 ESP32-C3 W5500 作为有线网关&#xff0c;运行 MicroPython。听上去简单&#xff0c;实操下来却是一场跨平台 编译环境 烧录流程的大混战。 为了避免你也在这些坑里打转&…

项目管理进阶:56页大型IT项目管理实践经验分享【附全文阅读】

此文档为大型IT项目管理实践经验分享目录概览&#xff0c;主要包含以下核心内容&#xff1a; 1. **整体介绍**&#xff1a;阐述了项目管理在IT领域的重要性&#xff0c;特别是针对产品经理与开发人员间的冲突和挑战&#xff0c;提出通过项目管理方法来提升工作效率。目标受众为…

一种在SQL Server中传递多行数据的方法

这是一种比较偷懒的方法&#xff0c;其实各种数据库对Json 支持的很好。sql server 、oracle都不错。所以可以直接传json declare 这是一个json varchar(max) set 这是一个json{"data":[{"code":"1","name":"啥1"},{"…

SOC-ESP32S3部分:25-HTTP请求

飞书文档https://x509p6c8to.feishu.cn/wiki/KL4RwxUQdipzCSkpB2lcBd03nvK HTTP&#xff08;Hyper Text Transfer Protocol&#xff09; 超文本传输协议&#xff0c;是一种建立在 TCP 上的无状态连接&#xff0c;整个基本的工作流程是客户端发送一个 HTTP 请求&#xff0c;说明…