跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

动态规划进阶:多状态模型与序列决策

讲解动态规划中的多状态模型,通过打家劫舍和买卖股票系列题目演示状态拆分与状态机转移方法。核心在于将复杂约束拆解为互斥状态,利用状态转移方程消除后效性。涵盖环形数组处理、冷冻期限制及交易次数限制等进阶场景,提供 C++ 代码实现与逻辑解析。

Stephaine Walsh发布于 2026/3/27更新于 2026/10/22103 浏览
动态规划进阶:多状态模型与序列决策

动态规划进阶:深度解析序列决策与状态机模型

在处理具有复杂限制条件的动态规划问题时,单一的状态定义往往难以涵盖所有决策分支。通过将每一天的局面拆解为多个互斥的快照(States),并利用状态机描述其间的转移逻辑,可以有效地消除后效性,使问题迎刃而解。

一、 '打家劫舍'模型:状态拆分的起点

这类题目的核心在于处理'相邻元素互斥'的约束。

题目:198.打家劫舍

198. 打家劫舍

  • 状态表示:
    • f[i]:表示偷到第 i 个位置时,确定偷 nums[i],此时的最大金额。
    • g[i]:表示偷到第 i 个位置时,确定不偷 nums[i],此时的最大金额。
  • 状态转移方程:基于'最后一步'的决策逻辑:
    • f[i] = g[i-1] + nums[i]:若偷当前房,前一间房必须处于'不偷'状态。
    • g[i] = max(f[i-1], g[i-1]):若不偷当前房,前一间房偷或不偷均可,取最大值即可。

初始化:f[0] = nums[0],g[0] = 0。

打家劫舍

代码实现:

class Solution {
public:
    int rob(vector<int>& nums) {
        //跟按摩师一样
        int n = nums.size();
        vector<int> f(n);
        auto g = f;
        f[0] = nums[0];
        for (int i = 1; i < n; ++i) {
            f[i] = g[i - ] + nums[i];
            g[i] = (f[i - ], g[i - ]);
        }
         (f[n - ], g[n - ]);
    }
};
1
max
1
1
return
max
1
1
题目:213.打家劫舍 II

打家劫舍 II

  • 核心痛点:第一间房与最后一间房首尾相连,产生约束冲突。
  • 化圆为线策略:通过'分类讨论'消除环形约束:
    • 情况 A:不考虑第一间房,在区间 [1, n-1] 执行上述逻辑。
    • 情况 B:不考虑最后一间房,在区间 [0, n-2] 执行上述逻辑。

结论:最终结果为 max(情况 B, 情况 A)。

转化

代码实现:

class Solution {
public:
    int rob(vector<int>& nums) {
        //处理首位房子后确定进行打家劫舍的区间
        //转化为打家劫舍 1(找到可以进行打家劫舍 1 的区间)
        int n = nums.size();
        if (n == 0) return 0;
        if (n == 1) return nums[0];
        return max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));
    }

    int rob1(vector<int>& nums, int left, int right) {
        if (left > right) return 0;
        int n = nums.size();
        vector<int> f(n);
        auto g = f;
        f[left] = nums[left];
        for (int i = left + 1; i <= right; ++i) {
            f[i] = g[i - 1] + nums[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[right], g[right]);
    }
};
题目:740.删除并获得点数

删除并获得点数

  • 转化思维:这道题看似新颖,实则是'打家劫舍'的变体。
  • 预处理:先统计每个数字出现的总分(点数 = 数字 × 出现次数)。
  • 建立映射:数字 i 的选取会导致 i-1 和 i+1 无法选取,这完全等同于'相邻房屋不能同时偷'的逻辑。
  • 解法:对统计后的点数数组执行 f[i] 与 g[i] 的状态转移。

示例

状态表示及状态转移方程

状态表示

代码实现:

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums[nums.size() - 1] + 1; //加上 0 这个位置
        //将原数组的值累加到 v 的对应位置,之后就可以转变为打家劫舍
        vector<int> v(n);
        for (int i = 0, j = nums[0]; i < nums.size();) {
            if (nums[i] == j) v[j] += nums[i++];
            else ++j;
        }
        //打家劫舍
        vector<int> f(n);
        auto g = f;
        f[1] = v[1], g[1] = 0;
        for (int i = 2; i < n; ++i) {
            f[i] = g[i - 1] + v[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n - 1], g[n - 1]);
    }
};

二、 '买卖股票'模型:多状态机的演进

股票系列题目引入了冷冻期和交易次数限制,状态机随之变得更加复杂。

题目:309.买卖股票的最佳时期含冷冻期

买卖股票的最佳时机含冷冻期

引入卖出后强制锁定 1 天的规则后,状态表示和状态转移方程如下:

  • 状态表示:第 i 天结束后,资产处于以下三者之一:
    • dp[i][0](买入状态):手中有一支股票。
    • dp[i][1](可交易状态):手里没股票,且今天没卖出,随时可以买。
    • dp[i][2](冷冻期状态):今天刚卖出股票,明天不能买。
  • 状态转移核心:
    • dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i])
    • dp[i][1] = max(dp[i-1][1], dp[i-1][2])
    • dp[i][2] = dp[i-1][0] + prices[i]

状态表示

根据状态机得出状态转移方程

转移方程

初始化:根据转移方程,只需要初始化 0 下标位置的值即可;

初始化

代码实现:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3, 0));
        //0 买入 1 可交易 2 冷冻期
        dp[0][0] = -prices[0]; //初始化第一个位置即可(1,2 都为 0)
        //根据状态机转换得出状态转移方程
        for (int i = 1; i < n; ++i) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return max(dp[n - 1][1], dp[n - 1][2]); //最大值不会在最后一天再买入
    }
};
题目:123.买卖股票的最佳时机 III

买卖股票的最佳时机 III

引入了'交易次数'这一关键属性标签。

  • 状态表示:
    • f[i][j]:第 i 天结束之后,完成了j次交易,此时处于'买入'状态下的最大利润。
    • g[i][j]:第i天结束之后,完成了j次交易,此时处于'卖出'状态下的最大利润。

状态表示

  • 状态转移的奥秘:
    • 卖出触发计数:g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i])。
    • 逻辑解析:当你卖出第 j 支股票时,你必须从'已完成 j-1 次交易且持股'的状态转移过来。卖出一瞬间,完成次数从 j-1 跃升为 j。
    • 初始化:为了避开非法路径(如未买入先卖出),除 g[0][0] = 0 和 f[0][0] = -prices[0] 外,其余均设为极小值(INF = 0x3f3f3f——整形最大值的一半)这样可以避免数据溢出。

状态转移方程

  • 如何理解 j-1

如果感到困惑是因为你在找'j = j + 1'这样的加法语句,实际在DP表中,g[i][j] 依赖 f[i-1][j-1] 就等同于执行了 j = j + 1。

  • 输入: 已经完成 j-1 次的状态。
  • 动作: 卖出(触发一次交易完成)。
  • 输出: 填入完成 j 次的状态格子里。

代码实现:

class Solution {
    const int INF = 0x3f3f3f; //整形最大值的一半
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(3, -INF)); //避免数据溢出
        auto g = f;
        f[0][0] = -prices[0], g[0][0] = 0; //初始化
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < 3; ++j) {
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]); //可以通过状态方程来处理边界问题
                g[i][j] = g[i - 1][j]; //处理第一列
                if (j >= 1) {
                    g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i]);
                }
            }
        }
        //答案在交易次数为 2 的那一行
        int ret = 0;
        for (int i = 0; i < 3; ++i) {
            ret = max(ret, g[n - 1][i]);
        }
        return ret;
    }
};

目录

  1. 动态规划进阶:深度解析序列决策与状态机模型
  2. 一、 “打家劫舍”模型:状态拆分的起点
  3. 题目:198.打家劫舍
  4. 题目:213.打家劫舍 II
  5. 题目:740.删除并获得点数
  6. 二、 “买卖股票”模型:多状态机的演进
  7. 题目:309.买卖股票的最佳时期含冷冻期
  8. 题目:123.买卖股票的最佳时机 III

更多推荐文章

查看全部
  • LLaMA-Factory 大语言模型微调框架实战指南
  • 冯诺依曼架构与操作系统设计基础
  • Gemini 学生计划实战指南:将免费 Pro 权限转化为 AI 求职竞争力
  • Spring Web MVC 入门:从概念到实践
  • 网络安全基础与黑客技能入门指南
  • 贪心算法实战:柠檬水找零、数组减半与最大数拼接
  • 公交网络辅助的无人机调度用于无线可充电传感器网络可持续充电
  • Virt-A-Mate (VAM) 虚拟现实交互软件技术特性介绍
  • AIGC 产品经理转行路径与核心能力体系详解
  • 利用 AI 智能体高效完成 C 语言及前端实训项目实战
  • 大模型提示词进阶指南:System Prompts 与数据集分析
  • 在 Cursor 中配置并使用 MCP 服务实战指南
  • 通义万相 2.1 的架构、能力与落地观察
  • C++ 二叉搜索树
  • Python 开源聊天机器人在电商外贸智能客服中的实战应用与架构解析
  • 滑动窗口算法详解:水果成篮问题解法
  • Whisper v0.2 语音转文字工具安装与使用指南
  • BILIVE 部署与运行常见问题排查指南
  • Java 中 Integer 对象比较陷阱解析
  • AI 产品经理转型指南:核心能力与实战框架

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online