蓝桥杯之状态机DP

为什么要学习这个算法呢,因为我看了一下上届蓝桥杯JAVAb组DP考的超级多,前面几题就有两道状态机DP,python组也有状态机DP,服啦服啦,不学不行。

以下对状态机的理解来自博主   Turing_Sheep
原文链接:https://blog.csdn.net/weixin_72060925/article/details/128767684

1.状态机DP含义:

一、通俗理解状态机DP
其实状态机DP只是听起来高级,其实我们之前做的所有关于DP的题几乎都算是状态机,为什么呢?

1、什么是状态机


大家继续向下看:

DP解决的是多决策的问题,那么我们可以把01背包问题画成下面的图:
按照正常的逻辑,我们一般都是从第一个物品开始看,决定选或者不选,然后再去看第二个物品。那么我们现在只看其中一个物品,这个物品无非就两个状态,在背包里,不在背包里。

其实这两个状态就构成了一个状态机。


写到这里,我们就可以通俗地理解一下状态机,状态机的作用就是记录一个事件所有可能的存在状态以及这些状态之间的联系。这个作用或许大家现在还会感到一些模糊,但是没关系,后面的例题中作者会进行详细地讲解,让大家深刻体会到。

2、什么是状态机DP
那么什么是状态机DP呢?这种DP方式和之前的DP有什么不同呢?

状态机DP就是在DP数组中专门开辟一个维度记录当前事件所处的状态,比如背包问题如果写成状态机DP的模式,我们会写成f [ i ] [ j ] [ 1 ] f[i][j][1]f[i][j][1]或者f [ i ] [ j ] [ 0 ] f[i][j][0]f[i][j][0],此时的1代表第i个物品在背包里,0说明第i个物品不在背包里。状态定义可以写作:在前i个物品中选,背包容量为j,且第i个物品在(或者不在)背包中的时候,我们所能携带的最大价值。

其实通过这个例子,大家就能明显地感觉到,在状态机模型中我们强调并记录当前事物的状态。

可是这样做有啥用呢?

作者可以给01背包问题加上一个情景,不能选择相邻的物品。此时如果还按照我们之前的做法,用f[i][j]来定义的话,你对某个物品的具体状态是模糊的。我们一般是用f[i-1][j]和f[i-1][j-v]+w来写转移方程,但是你无法判断第i-1个物品是否在背包里,也就是说你不知道第i-1个物品所处的状态。

但是此时如果按照状态机模型来写,特意强调一下某个物品的状态,我们就可以利用第三维度的0和1来判断能否选当前的物品。

2.什么时候用状态机DP

那么此时可以总结一下什么时候用状态机DP,当某个事件的状态影响到后面事件的决策的时候,我们需要在状态定义的时候记录事件状态,即状态机DP。
 

刷完以下题,相信我们对状态机模型有一个明确的认识!

2.蜗牛

题目来源 1.蜗牛 - 蓝桥云课 (lanqiao.cn)

到达第i根竹竿底部有两种方式:我们可以从第i-1根竹竿沿X轴走过来,也可以从i-1根竹竿瞬移之后再下来,但是到达第i-1根竹竿的舜移点的最短时间我们是不清楚的,因此我们可以考虑状态机DP,f[i][0]表示到达第i根竹竿底部的最短时间,f[i][1]表示到达第i根竹竿瞬移点的最短时间(此处指的是a[i])。

状态转移一下子就清楚了,下面我们来看一下代码:


import java.util.Scanner;

public class 蜗牛 {
    static int N=100010;
    static double[]a=new double[N];
    static double[]b=new double[N];
    static int n;
    static double[][]f=new double[N][2];
    static double[]x=new double[N];

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        for (int i = 1; i <=n ; i++) {
            x[i]=sc.nextInt();
        }
        for(int i=1;i<n;i++){
            a[i]=sc.nextInt();
            b[i+1]=sc.nextInt();
        }
        f[1][0]=x[1];
        f[1][1]=f[1][0]+a[1]/0.7;
        for (int i = 2; i <=n ; i++) {
           f[i][0]=Math.min(f[i-1][0]+(x[i]-x[i-1]),f[i-1][1]+b[i]/1.3);

           if(b[i]>a[i]){
               f[i][1]=Math.min(f[i-1][1]+(b[i]-a[i])/1.3, f[i-1][0]+(x[i]-x[i-1])/1+a[i]/0.7);
           }else {
                f[i][1]=Math.min(f[i-1][1]+(a[i]-b[i])/0.7,f[i-1][0]+(x[i]-x[i-1])/1+a[i]/0.7);
           }
        }

        System.out.printf("%.2f",f[n][0]);







    }
}

3.数组分割

题目链接:

 1.数组分割 - 蓝桥云课 (lanqiao.cn)

题目大概意思就是从n个数中选出数字的数字总和是偶数的方案数,

如果当前数字是偶数,我们可以根据上一步的状态考虑选或不选,如果上一步数字总和是偶数,选不选都可以,如果上一步总和是奇数,我们可以考虑选择当前数字;

奇数同理。

想到这里,我们就会想到上一步的数字总和是奇数还是偶数对于我们的当前状态的选择非常重要,因此我们可以考虑状态机DP,f[i][0]表示考虑前i个数字,且当前数字总和为偶数的方案数; f[i][1]表示考虑前i个数字,且当前数字是奇数的方案数;

因此我们可以写出转移方程

f[0][0] = 1;
	for (int i = 1; i <= n; ++i) {
		if (a[i] % 2) {
			f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
			f[i][1] = (f[i - 1][1] + f[i - 1][0]) % mod;
		} else {
			f[i][0] = (f[i - 1][0] * 2) % mod;
			f[i][1] = (f[i - 1][1] * 2) % mod;
		}
	}

怎么样,是不是对状态机类型的DP有了更清晰的认识了呢,我们再看一下什么时候用它?

这句话需要记在脑子里。

当某个事件的状态影响到后面事件的决策的时候,我们需要在状态定义的时候记录事件状态,即状态机DP。
 

4.大盗阿福

题目来源于Acwing。

是不是很熟悉,没错就是力扣上的打家劫舍原题,假如你作为一个小偷,如果偷相邻的两给屋子里的东西,就会触发警报,题目让求你不被抓时候偷的物品的最大价值。

考虑当前状态偷还是不偷,我们是不清楚当前这个店铺是能不能偷的,因为我们不知道我们上次偷的与这个相不相邻,所以前面的物品的状态就影响到了当前状态的决策,因此我们考虑状态机DP,f[i][0]表示考虑前i家店铺,第i家店铺没偷;   f[i][1]表示考虑前i家店铺,偷了第i家店铺。

分析完之后,状态转移方程就呼之欲出了!

f[i,0]=Max(f[i-1,0],f[i-1,1])

f[i,1]=f[i-1,0]+w[i];

5.买卖股票

第i天买还是买还是卖股票我们不清楚,因为我们不知道我们手里是否有股票,有股票我们就必须将当前股票卖出去才能再次购入股票,没股票我们可以选择是否购入股票。

因此状态表示:

f[i][j][1]表示考虑前i天,正在进行第j次交易,手里有股票;

f[i][j][1]表示考虑前i天,正在进行第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])

6.买卖股票||

题目来源Acwing,是上一题的进阶版,增加的约束是卖出股票后,不能在第二天买入股票(冷冻期为一天)

看一下状态机表示:

分析完这个,状态表示方程就轻而易举了!!!

7.练习真题---松散子序列

怎么样对状态机DP有了清晰的认识了,光说不练可不行,下面来到DP真题,题目来源于第十四届python组真题,链接1.松散子序列 - 蓝桥云课 (lanqiao.cn)

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值