状态机模型

状态机代表一系列有序的事件,我们通过状态机能够将一个复杂的状态拓展为几个简单过程

状态机是一种另类的状态表示方式,实际上是我们将每一个状态拓展成一个过程

任何一个方案都能唯一对应一个状态机

简单的说,我们可以通过状态机将一个复杂、混沌的状态细分成几个清晰的状态

状态机在任意时刻的状态大多为01

状态机需要考虑入口出口

大盗阿福

阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。

这条街上一共有 n 家店铺,每家店中都有一些现金。

阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。

作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。

他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?

1T50,
1n105

题解:线性DP——状态机模型

  • 第一种非线性dp

  • 状态表示:f[i]代表只从前i个店铺中偷窃,能够获得的最大价值

  • 状态属性:MAX

  • 状态计算:

  1. 选择不偷窃第i个店铺:f[i1]
  2. 选择偷窃第i个店铺,那我们一定无法偷窃第i1个店铺:f[i2]+w[i]

f[i]=max(f[i1],f[i2]+w[i])

  • 状态初始:f[0]=0,f[1]=w[1]

  • 答案呈现:f[n]

  • 第二种线性dp(状态机模型):

  • 状态表示:

f[i][0/1]代表只从前i个店铺中选,且0代表选择第i家店铺,1代表不选第i家店铺所能获得的最大价值

  • 状态属性: MAX

  • 状态计算:按照路径来划分集合,若选择某店铺,将其状态表示为1,若不选某个店铺,将其状态表示为0

image-20230517002639953
  1. 选择第i个店铺,那么我们肯定不选择第i1家店铺,即只有一条边从状态0通向状态1,即f[i1][0]+w[i]
  2. 不选择第i个店铺,那么第i1家店铺可以选择也可以不选择,即有两条路径可以从其他状态到状态0,即max(f[i1][0],f[i1][1])

f[i][1]=f[i1][0]+w[i]f[i][0]=max(f[i1][0],f[i1][1])

  • 状态初始:f[1][0]=0,f[1][1]=w[1]

  • 状态机入口:1,状态机出口:01

  • 答案呈现:max(f[n][0],f[n][1])

//第一种dp
const int N = 2e5 + 10, M = 4e5 + 10;

int n;
int f[N];
int w[N];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> w[i];
    for (int i = 0; i <= n; ++i)
        f[i] = 0;
    f[0] = 0;
    f[1] = w[1];
    for (int i = 2; i <= n; ++i)
        f[i] = max(f[i - 1], f[i - 2] + w[i]);
    cout << f[n] << endl;
}
//第二种dp
const int N = 2e5 + 10, M = 4e5 + 10;

int n;
int w[N];
int f[N][2];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> w[i], f[i][1] = f[i][0] = 0;
    for (int i = 1; i <= n; ++i)
    {
        f[i][1] = max(f[i][1], f[i - 1][0] + w[i]);
        f[i][0] = max(f[i - 1][1], f[i - 1][0]);
    }
    cout << max(f[n][0], f[n][1]) << endl;
}

股票买卖 IV

给定一个长度为 n 的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润,你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

一次买入卖出合为一笔交易

1n105

1k100

题解:线性dp——状态机模型 O(nk)

  • 状态表示

f[i][j][0/1]代表只在前i天中选择,且已经完成j笔交易,且1代表在第i天手中有股票,0代表第i天手中没有股票的最大收益

  • 状态属性:MAX

  • 状态计算:

image-20230522210348655
  1. 如果第i天手中有股票:f[i][j][1]=max(f[i1][j][1],f[i1][j][0]w[i])
  2. 如果第i天手中无股票:f[i][j][0]=max(f[i1][j][0],f[i1][j1][1]+w[i])
  • 状态优化:

因为是线性的,所以容易发现第一维可以优化掉,倒序遍历交易数量(第二维)

  • 状态初始:f[i][0][0]=0/f[0][0]=0

  • 答案呈现:max(f[n][j][0],f[n][j][1])

const int N = 2e5 + 10, M = 1e2 + 10;

int n, m;
int w[N];
int f[M][2];
// f[i][j][0/1]代表只在前i天中选择,且已经完成j笔交易,且1代表在第i天手中有股票,0代表第i天手中没有股票的最大收益

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> w[i];
    memset(f, -0x3f, sizeof f);
    f[0][0] = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = m; j >= 0; --j)
        {
            f[j][1] = max(f[j][1], f[j][0] - w[i]);
            if (j >= 1)
                f[j][0] = max(f[j][0], f[j - 1][1] + w[i]);
        }
    int ans = -INF;
    for (int j = 0; j <= m; ++j)
        ans = max({ans, f[j][0], f[j][1]});
    cout << ans << endl;
}

股票买卖Ⅴ

给定一个长度为 n 的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票 (即冷冻期1 天)。

1n105

题解:线性dp——状态机模型

  • 状态表示:

f[i][0/1/2]:代表只在前i天中选,且0代表第i天手中有股票,1代表第i天是卖出股票后的第一天,2代表第i天卖出股票后的第k天(k>=2)能够获得的最大利润

  • 状态属性:MAX

  • 状态计算:

image-20230522212131316

f[i][0]=max(f[i1][0],f[i1][2]w[i])
f[i][1]=f[i1][0]+w[i]
f[i][2]=max(f[i1][1],f[i1][2])

  • 状态机出口:1/2

  • 状态机入口:2

  • 状态初始:f[0][2]=0,f[i][j]=INF

  • 答案呈现:max(f[n][1],f[n][2])

const int N = 2e5 + 10, M = 1e2 + 10;

int n;
int w[N];
int f[N][3];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> w[i];
    for (int i = 0; i <= n; ++i)
        f[i][1] = f[i][0] = -INF;
    f[0][2] = 0;
    for (int i = 1; i <= n; ++i)
    {
        f[i][0] = max(f[i - 1][0], f[i - 1][2] - w[i]);
        f[i][1] = f[i - 1][0] + w[i];
        f[i][2] = max(f[i - 1][1], f[i - 1][2]);
    }
    cout << max(f[n][1], f[n][2]) << endl;
}

设计密码

你现在需要设计一个密码 SS 需要满足:

  • S 的长度是 n
  • S 只包含小写英文字母
  • S 不包含子串 T

例如:abcabcdeabcde 的子串,abd 不是 abcde 的子串。

请问共有多少种不同的密码满足要求?

由于答案会非常大,请输出答案模 109+7 的余数。

1n50

题解:KMP + 状态机模型 O(26n3)

  • 状态表示:

f[i][j]:代表已经生成了i位密码,且第i位密码已经匹配到子串中位置为j,即第i+1位密码正在和子串中j+1位进行匹配的方案数

image-20230522223508487
  • 状态属性:数量

  • 状态转移:

因为字符串S中只存在小写字母,所以S的位置i+1处的字符只有26种可能性

我们不妨枚举位置i+1会出现的所有小写字母k

如果第i+1位的字母k不等于T[j+1],我们总可以利用KMPnext数组找到一个位置u使得k=T[u+1]

我们令u:=u+1,字符串T的长度为m,因为S中不能含有T,所以只有u<m的时候才能转移

状态转移方程:f[i+1][u]=(f[i+1][u]+f[i][j])  %  mod (u<m)

时间复杂度:O(26n)

  • 状态初始:f[0][0]=1

  • 答案呈现:f[n][i]

const int N = 50 + 10, M = 4e5 + 10;

int n;
int ne[N];
int f[N][N];

void solve()
{
    cin >> n;
    string t;
    cin >> t;
    int m = t.length();
    t = " " + t;
    ne[1] = 0;
    for (int i = 2, j = 0; i <= m; ++i)
    {
        while (j && t[i] != t[j + 1])
            j = ne[j];
        if (t[i] == t[j + 1])
            j++;
        ne[i] = j;
    }
    f[0][0] = 1;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            for (char k = 'a'; k <= 'z'; ++k)
            {
                int u = j;
                while (u && t[u + 1] != k)
                    u = ne[u];
                if (t[u + 1] == k)
                    u++;
                if (u < m)
                    f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
            }
    int ans = 0;
    for (int j = 0; j < m; ++j)
        ans = (ans + f[n][j]) % mod;
    cout << ans << endl;
}
posted @   Zeoy_kkk  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示