动态规划之状态机模型详细介绍和例题练习

本文介绍了状态机模型的概念,将其定义为现实事物运行规则的数学抽象,通过自动门的例子阐述了状态、条件、动作和次态四个要素。接着,利用状态机模型解决了一系列算法问题,如打家劫舍、买卖股票的最佳时机等,展示了状态机在动态规划中的应用。文章还提供了Java代码示例,展示如何构建状态转移方程来解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

状态机模型

概述

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

  • 以下关于状态机介绍的内容参考自知乎:文章链接
  • 先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个 open 和 closed 。
  • 状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。
    在这里插入图片描述
  • 自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed 。
  • 状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于自动门,给定初始状态 closed ,给定输入“开门”,那么下一个状态时可以运算出来的。
  • 这样状态机的基本定义我们就介绍完毕了。重复一下:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。

  • 详解如下:
    • 现态:是指当前所处的状态。
    • 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
    • 动作:条件满足后执行的动作,动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
    • 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

状态机模型例题

198. 打家劫舍

188. 买卖股票的最佳时机 IV

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

设计密码
在这里插入图片描述

  • 状态定义:
    • dp[i][j]:写到密码串前 i 个字母,且当前跳到 kmp 的第 j 个状态的方案数量
  • 状态转移方程: dp[i + 1][u] = dp[i + 1][u] + dp[i][j]。这里的状态定义是已经有的长度,不包括当前枚举的字母。 也可以理解为原来的 dp[i][j] = dp[i-1][j] 这类的,都是拿后继更新前驱。然而本题是用当前这个后继状态来更新它能到达的所有后继状态。
  • Java实现代码如下:
import java.util.*;

public class Main {
    static int N = 55;
    static int MOD = (int) (1e9+7);
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        int n = 0;
        String s = "";
        char[] str = new char[26];

        n = in.nextInt();
        in.nextLine();
        s = in.nextLine();
        str = s.toCharArray();


        System.out.println(solve(n, str));
        in.close();
    }

    private static int solve(int n, char[] str) {
        int len = str.length;
        int[] next = new int[len];
        int[][] dp = new int[N][N];

        // 求next数组
        for (int i = 2, j = 0; i < len; i++) {
            while (j > 0 && str[i-1] != str[j]) {
                j = next[j];
            }
            if (str[i-1] == str[j]) {
                j++;
            }
            next[i] = j;
        }

        dp[0][0] = 1;            // 状态初始化,一个字母都没有,也是一种方案
        for (int i = 0; i < n; ++i) {
            // 枚举j的位置,不包括子串的末尾len
            for (int j = 0; j < len; ++j) {
                for (char k = 'a'; k <= 'z'; ++k) {     // i的26种选法,也可视为状态j的26种出边
                    int u = j;
                    // str[i]失配跳next,得到最后的kmp位置,在这不论是单个字母还是字符串,kmp都是while,
                    // 在此是一个字母就只判断一次,最终停下来u大多都是0吧
                    while (u > 0 && k != str[u]) {
                        u = next[u];
                    }
                    if (k == str[u]) {
                        u ++;
                    }
                    if (u < len) {
                        dp[i + 1][u] = (dp[i + 1][u] + dp[i][j]) % MOD;   // 采用后继更新前驱节点
                    }
                }
            }
        }

        int res = 0;
        for (int i = 0; i < len; ++i) {
            res = (res + dp[n][i]) % MOD;
        }
        return res;
    }
}

你知道的越多,你不知道的越多。
有道无术,术尚可求,有术无道,止于术。
如有其它问题,欢迎大家留言,我们一起讨论,一起学习,一起进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值