【动态规划】状态机模型

A、抛砖引玉 - AcWing 1049. 大盗阿福

AcWing 1049. 大盗阿福
在这里插入图片描述

本题比较简单,主要起到一个抛砖引玉的作用。
我们可以很容易地想到转移方程: f [ i ] = m a x ( f [ i 1 ] , f [ i 2 ] + w [ i ] ) f[i] = max(f[i - 1] ,f[i - 2]+w[i])

我们尝试使用状态机模型解决这一类问题。
什么是状态机模型?

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

实际上就是我们利用一个类似图论的方法,建图,点表示各各状态,边表示状态转移时要达成的条件,一旦条件成立,就自动转移,所以状态机也是一个自动机。(状态机多运动于游戏等有各种状态转移之中)
例如本题,我们可以把整个模型抽离出两个状态,阿福只有这两个状态:选择当前(最后)店铺,不选择当前店铺。并且分析转移达成的条件,画出边。然后根据这个图非常清晰不重不漏地设计出状态转移方程。
在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 5000007;

int n, m;
int f[N][2];
int a[N];
int main()
{
    int t;
    scanf("%d", &t);
    while(t -- )
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++ i)
            scanf("%d", &a[i]);
        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] + a[i];
        }
        printf("%d\n", max(f[n][0],f[n][1]));
    }
    return 0;
}

然后我们来看接下来的几个例题:

B、AcWing 1057. 股票买卖 IV

AcWing 1057. 股票买卖 IV
在这里插入图片描述
画出状态机模型
这里由于题目要求只能进行k次交易,所以我们设置一个二维数组
在这里插入图片描述
然后辅以闫氏DP分析法
在这里插入图片描述
注意边界的设置:

  • 初始化为0->该状态合法,最开始可以从这里转移过来
  • 初始化为-INF/INF表示不希望用到它,最开始不能从它转移过来
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 100007;

int n, m;
int w[N];
int f[N][110][2];

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++ i)
    scanf("%d", &w[i]);
    
    memset(f, 0xcf, sizeof f);
    for(int i = 0; i <= n; ++ i)
        f[i][0][0] = 0;
    
    for(int i = 1; i <= n; ++ i)
        for(int k = 1; k <= m; ++ k)
        {
            f[i][k][0] = max(f[i - 1][k][0], f[i - 1][k][1] + w[i]);
            f[i][k][1] = max(f[i - 1][k][1], f[i - 1][k - 1][0] - w[i]);
        }
    int res;
    for(int i = 0; i <= m; ++ i)
        res = max(res, f[n][i][0]);
    printf("%d\n", res);
    return 0;
}

C、AcWing 1058. 股票买卖 V

在这里插入图片描述
由于买完股票之后有一天的冷冻期, 所以我们把手里没股票根据题意拆成两种情况,然后分析所有的点与边。
在这里插入图片描述
初始化:我们最开始只能从手里无股票两天及以上的这一种情况出发,因为只有这一种可以开始拓展并且符合题意。注意我们必须初始化 f[0][2] = 0; f[0][0] = f[0][1] = -INF;,因为第一次购买股票是要花钱的,如果不将边界设置为-INF,由于取max操作会导致一直不买。
最后答案在 f [ n ] [ 1 ] , f [ n ] [ 2 ] f[n][1], f[n][2] 之中取最大值,因为有可能股票一直下跌不买为最优,这样2就不会转移到1,出口就不再1了。

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

using namespace std;

const int N = 500007, INF = 0x3f3f3f3f;

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

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i)
        scanf("%d", &w[i]);
    
    //0:手里有股票1:手里没股票1天2:手里没股票2天及以上
    f[0][2] = 0;
    f[0][0] = f[0][1] = -INF;
    
    for(int i = 1; i <= n; ++ i)
    {
        f[i][0] = max(f[i - 1][2] - w[i], f[i - 1][0]);
        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;
}

D、AcWing 1052. 设计密码

E、AcWing 1053. 修复DNA

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/108237332