状态机模型

 大盗阿福【线性DP+状态机模型DP+滚动数组优化】

题目 大盗阿福icon-default.png?t=M85Bhttp://ybt.ssoier.cn:8088/problem_show.php?pid=1301

解析icon-default.png?t=M85Bhttps://www.acwing.com/solution/content/55012/

 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,INF=0x3f3f3f3f;
int w[N];
int f[N][2]; 
void solve()
{
	int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>w[i];
    for(int i=1;i<=n;i++)
    {
    	f[i][0]=max(f[i-1][1],f[i-1][0]);
    	f[i][1]=f[i-1][0]+w[i];
	}
	cout<<max(f[n][0],f[n][1])<<endl;
}
int main()
{
	int t;
	cin>>t;
    while(t--)
    {
    	solve();
	}
}

 股票买卖 IV【线性DP+状态机模型DP】

 题目 股票买卖 IVicon-default.png?t=M85Bhttps://www.acwing.com/problem/content/description/1059/

解析icon-default.png?t=M85Bhttps://www.acwing.com/solution/content/55037/

 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=110;
int f[N][M][2],w[N];
int main()
{
    int n,k;
    cin>>n>>k;
    memset(f,-0x3f,sizeof f);
    f[0][0][0]=0; //初始状态f[0][0][0]
    for(int i=1;i<=n;i++)
    cin>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=k;j++)
        {
            f[i][j][0]=f[i-1][j][0];
            if(j)f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][1]+w[i]);
            f[i][j][1]=max(f[i-1][j][1],f[i-1][j][0]-w[i]);
        }
    }
    int res=-1e9;
    for(int i=0;i<=k;i++)
    res=max(res,f[n][i][0]);//目标状态f[n][j][0]
    cout<<res<<endl;
}

股票买卖 V【线性DP+状态机模型DP】 

题目 股票买卖 V icon-default.png?t=M85Bhttps://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/

 解析icon-default.png?t=M85Bhttps://www.acwing.com/solution/content/55147/

 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int w[N];
int f[N][3]; 
int main()
{
	int n;
	cin>>n;
	memset(f,-0x3f,sizeof f);
	f[0][0]=0;
	for(int i=1;i<=n;i++)
	cin>>w[i];
	for(int i=1;i<=n;i++)
	{
		f[i][0]=max(f[i-1][0],f[i-1][2]);
		f[i][1]=max(f[i-1][1],f[i-1][0]-w[i]);
		f[i][2]=f[i-1][1]+w[i];
	}
	cout<<max(f[n][0],f[n][2])<<endl;
}

 设计密码【线性DP+KMP自动机模型】

 解析1icon-default.png?t=M85Bhttps://blog.csdn.net/yl_puyu/article/details/110135553?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166808287816800192259602%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166808287816800192259602&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-110135553-null-null.142^v63^control,201^v3^control_2,213^v2^t3_control1&utm_term=%E8%AE%BE%E8%AE%A1%E5%AF%86%E7%A0%81&spm=1018.2226.3001.4187

 解析2icon-default.png?t=M85Bhttps://www.acwing.com/solution/content/55449/

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 55, mod = 1e9 + 7;

int n, ne[N], f[N][N];
char s[N];

/*
    KMP + 状态机:
        对于本题来说,要保证设计的密码中不包含子串T,我们可以想到KMP的目的--->快速找到子串出现的起始位置!
        因此对于本题的第三个要求,不能有子串T,即表示KMP算法中只要j走不到模板串的末尾就一定不包含子串T!


    1. 这也是一个状态机问题!对于状态机问题,我们首先要确定状态:
        状态就是:KMP中j来回可以跳到的位置!(即j可以取0 ~ m共m + 1种状态,入口就是0号位置)

    2. 考虑j可以跳的情况:会发现j可以跳的位置是完全由模式串i的位置决定的,i位置对应的字符可以取(a ~ z),因此
        对于i的每种选法,j都有一个唯一跳法使得i和j+1位置对应的字符相等!
    也就是说对于模板串的j对应的m + 1种状态,每种状态都有26种选法(a ~ z),因此:我们就可以建一个具有m + 1个点,
        每个点都有26条边的图!

    3. 我们可以从入口0号位置开始跳,跳到的每一个位置都是一个合法的字符串(注意:不能跳到m位置,跳到m位置就表示包含子串T了)

    4. 也就是说:每一个合法的字符串,都可以一一对应到一种KMP上的状态机的走法。
              反过来说:每一种合法的KMP走法,都一一对应一个合法的字符串!
              即:合法字符串的方案数等价于KMP的状态机走法数!我们只要计算统计KMP的j的走法数即可!

    因此:最终的答案就是从入口开始走,不走到m位置,一共走n步(设计的密码S的长度限制),一共有多少种不同的路线!

        具体来说:对于模式串i的每一种选法方案,都会对应模板串j的一种走法;j的每一种走法都会对应模式串i的一种选法的合法方案!

    --------------------------------------- DP分析 ---------------------------------------------------

    状态表示:f[i][j]表示 “已经” 枚举了第i个字母(或第i步),处于j状态(即KMP中j可以跳到的0 ~ m+1种位置状态)的方案数!
    状态计算:考虑f[i][j]是有哪个状态(记该状态为A)转移过来的(简而言之:就是当前状态的方案数是由各类A的方案数之和组成)
        设j状态是由u状态跳过来的,即
                    f[i][j] = f[i - 1][u]
        解释:当前第i步处于j状态自然是从第i-1步跳过来的,并且是从可以跳到j状态的u状态跳过来的!
        注意:对于第i步,由于i位置26种选法的不同,j状态是由KMP动态移动求得,可能会出现j状态多次指向同一个位置,
              所以,准确来说应该是:f[i][j] += f[i - 1][u]

        由于模式串我们是从0开始的(即枚举的模式串的每一个位置的选法的下标),而对于步数来说是从1开始的:
            因此,准确来说应该是:f[i + 1][j] += f[i][u]

        对应到下方代码:u状态是从j状态跳过来的,因此:f[i + 1][u] += f[i][j]

    分析完毕!
    
    (每一个状态都包含了很多种走法)
 
    最终答案:f[n][j]的和(j可取0,1,2 ... m-1)

*/

int main()
{
    cin >> n >> s + 1;
    int m = strlen(s + 1);

    for (int i = 2, j = 0; i <= m; i++)
    {//j表示匹配成功的长度,i表示s数组中的下标,因为s数组的下标是从1开始的,只有1个时,一定为0,所以i从2开始
        while (j && s[i] != s[j + 1])
            j = ne[j];        //如果不行可以换到next数组
        if (s[i] == s[j + 1])
            j++;    //成功了就加1
        ne[i] = j;  //对应其下标
    }
    
    f[0][0] = 1;
    for (int i = 0; i < n; i++)               // 枚举步数为i + 1(枚举下标为i的字符,i从0开始)
        for (int j = 0; j < m && j <= i; j++) // 枚举j可以跳到的状态(0 ~ m共m种状态,必然不能跳到m)
            for (char k = 'a'; k <= 'z'; k++)
            { // 枚举模式串i可以取的 a-z 26种状态!
                // 对于模式串i的每种选法,利用KMP找到j可以跳到的位置u
                int u = j;
                while (u && k != s[u + 1])
                    u = ne[u];
                if (k == s[u + 1])
                    u++;
                // 只要没有跳到m及m之后就是合法方案
                if (u < m)
                    f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
            }

    int res = 0;
    for (int i = 0; i < m; i++)
        res = (res + f[n][i]) % mod;
    cout << res << endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值