状态机模型与题目详解acm_t


#写在前面

状态机描述的是一系列有顺序的事件
状态机可以把事件中的各个状态描述清楚
01背包对每个物品只有选与不选,这不是一个连续的过程

状态机描述的是一个过程,不是结果

先有几种状态(几个点),每个状态之间有一些有向边(通道),状态可以通过通道转换,每一次转换相当于走了一步。
当走过k步时,可以有很多种走法。

状态机和状态压缩dp是并列的两个知识点

##大盗啊福

https://www.acwing.com/problem/content/1051/
传统的思考方式在这里插入图片描述
在这里插入图片描述
分解对于每个物品(每一步)的状态:
在这里插入图片描述
状态机的思考方式:
在这里插入图片描述
图论拆点

----c++版

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010,inf=0x3f3f3f3f;
int n;
int w[N],f[N][2];

int main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++)scanf("%d",&w[i]);
        f[0][0]=0;f[0][1]=-inf;//入口指向0,虚拟边界不可选
        for(int i=1;i<=n;i++){//对于每个物品(每一步),都有两种状态
            f[i][0]=max(f[i-1][0], f[i-1][1]);
            f[i][1]=f[i-1][0]+w[i];
        }
        printf("%d\n", max(f[n][0], f[n][1]));
        
    }
    return 0;
}

##股票买卖4

https://www.acwing.com/problem/content/1059/
在这里插入图片描述

在这里插入图片描述

----c++版

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010,M=110;
int n,m;
int w[N];
int f[N][M][2];

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d", &w[i]);
    memset(f, -0x3f, sizeof f);
    for(int i=0;i<=n;i++)f[i][0][0]=0;//如果一次交易都没有进行,手中一定是无货的
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            f[i][j][0]=max(f[i-1][j][0], f[i-1][j][1]+w[i]);
            f[i][j][1]=max(f[i-1][j][1], f[i-1][j-1][0]-w[i]);
        }
        
    int res=0;
    //完整交易表示最终手中无货
    for(int i=0;i<=m;i++)res=max(res, f[n][i][0]);
    printf("%d\n", res);
    return 0;
}

##股票买卖5

https://www.acwing.com/problem/content/1060/
在这里插入图片描述

----c++版

//三个状态的
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100210, inf=0x3f3f3f3f;
int n;
int w[N];
int f[N][3];

int main(){
    scanf("%d", &n);
    for(int i=1;i<=n;i++)scanf("%d", &w[i]);
    f[0][0]=f[0][1]=-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]);
    }
    printf("%d\n", max(f[n][1],f[n][2]));//当一直在亏钱的时候,最优解是一次交易都不做
    return 0;
}

##设计密码

https://www.acwing.com/problem/content/1054/

在这里插入图片描述

----c++版

#include<iostream>
#include<algorithm>
//kmp和状态机
//kmp的本质是一个j指针在字符串上到处乱跳
//匹配时j永远跳不到子串的末尾,说明没有子串
using namespace std;
const int N=55,mod=1e9+7;
int n,m;
char str[N];
int f[N][N];
int ne[N];//这个要写在外面
#include<cstring>
int main(){
    cin>>n>>str+1;
    m= strlen(str+1);
    for(int i=2,j=0;i<=m;i++){
        while (j && str[i] != str[j + 1]) j = ne[j];
        if (str[i] == str[j + 1]) j ++ ;
        ne[i] = j;
    }
    f[0][0]=1;
    for(int i;i<n;i++)
        for(int j=0;j<m;j++)
            for(char k='a';k<='z';k++){
                int u=j;
                while(u&&k!=str[u+1])u=ne[u];
                if(k==str[u+1])u++;
                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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值