DP模型——状态机模型

状态机

简单来说:把点扩成一个过程

点是指某一个特定的状态,过程是指可能存在多个点之间相互转化

在进行的过程中除了定义好状态,还要对状态入口和出口进行思考确定

最后总结一句:绝了

大盗阿福

有 N 加店铺,不能抢劫两家相邻的店铺,问抢劫的最大价值是多少

一般做法

状态表示

f[i] 表示前 i 家的最大收益

状态计算

对于第 i 家店铺来说,当不抢这家店铺的最大价值显然就是f[ i - 1 ]

当抢这家店铺的时候,我们就不能抢第 i - 1 家店铺了,那么我们只能从前i - 2家店铺来考虑

那么方程就是 f [ i ] = m a x ( f [ i − 1 ] , f [ i − 2 ] + w [ i ] ) f[i]=max(f[i - 1], f[i -2]+w[i]) f[i]=max(f[i1],f[i2]+w[i])

Code

// Problem: 大盗阿福
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/1051/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Code by: ING__
// 
// Edited on 2021-07-29 00:03:23

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

const int N = 1e5 + 10;

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

int main(){
	cin >> T;
	while(T--){
		cin >> n;
		for(int i = 1; i <= n; i++){
			cin >> w[i];
		}
		
		memset(f, 0, sizeof f);
		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;
	}
	return 0;
}

状态机

我们如果使用上面那种方法是不知道上一家店铺中有没有被抢了,只能用往前两层来转移

我们将f[i] 分解为两个状态f[i][0], f[i][1],第一个表示未选这个店铺,第二个表示选择了这个店铺

根据题意,我们可以将状态的跳转写成:
在这里插入图片描述

我们从i转移到i+1,相当于在这两个状态之间走了一条边;那么任何一个选择方案,都能对应这个状态机里面的一个长度为 n 的一个走法,反过来一个长度为 n 的走法都相当于一个方案

状态表示

f[i, 0], f[i, 1]表示所有走了i步,且当前状态为j的所有走法

状态计算

  • f[i, 0]
    • 从0到0
      • f[i - 1, 0]
    • 从1到0
      • f[i - 1, 1]
  • f[i, 1]
    • 从0到1
      • f[i - 1, 0] + w[i]

Code

// Problem: 大盗阿福
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/1051/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Code by: ING__
// 
// Edited on 2021-07-29 00:03:23

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

const int N = 1e5 + 10;

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

int main(){
	cin >> T;
	while(T--){
		cin >> n;
		for(int i = 1; i <= n; i++){
			cin >> w[i];
		}
		
		memset(f, 0, sizeof f);
		f[1][1] = w[1]; // 比从0初始化更好想
		f[1][0] = 0;
		for(int i = 2; 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;
	}
	return 0;
}

股票买卖Ⅳ

我们一次只能持有一只股票,不能手里有再买,不能没有就卖

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

状态表示

f[i, j, 1]表示所有前i天且正在进行了j次交易且手中有货的收益

f[i, j, 0]表示所有前i天且正在进行了j次交易且手中无货的收益

状态计算

请添加图片描述

f [ i ] [ j ] [ 0 ] = m a x ( f [ i − 1 ] [ j ] [ 0 ] , f [ i − 1 ] [ j ] [ 1 ] + w [ i ] ) f [ i ] [ j ] [ 1 ] = m a x ( f [ i − 1 ] [ j ] [ 1 ] , f [ i − 1 ] [ j − 1 ] [ 0 ] − w [ i ] ) 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]) f[i][j][0]=max(f[i1][j][0],f[i1][j][1]+w[i])f[i][j][1]=max(f[i1][j][1],f[i1][j1][0]w[i])

Code

// Problem: 股票买卖 IV
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1059/
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Code by: ING__
// 
// Edited on 2021-07-29 12:47:35

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};

const int N = 100010;
const int M = 110;

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

int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> 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 ans = 0;
	for(int i = 0; i <= m; i++)
		ans = max(ans, f[n][i][0]);
		
	cout << ans;
	
	return 0;
}

股票买卖Ⅴ

给定 N 只股票,一次只能持有一只股票,不能手里有再买,不能没有就卖

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

卖出后,无法在第二天接着买入

状态表示

在这里插入图片描述

状态计算

f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 2 ] − w [ i ] , f [ i − 1 ] [ 0 ] ) f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + w [ i ] f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 2 ] , f [ i − 1 ] [ 1 ] ) 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][2], f[i - 1][1]) f[i][0]=max(f[i1][2]w[i],f[i1][0])f[i][1]=f[i1][0]+w[i]f[i][2]=max(f[i1][2],f[i1][1])

Code

// Problem: 股票买卖 V
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1060/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Code by: ING__
// 
// Edited on 2021-07-29 12:47:40

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

using namespace std;

const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;

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

int main(){
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> w[i];
    }

    f[0][1] = f[0][0] = -INF;
    f[0][2] = 0; // 入口是合法的;并且下一天能买

    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][2], f[i - 1][1]);
    }

    cout << max(f[n][1], f[n][2]);

    return 0;
}

设计密码

拥有 m 个状态(忙完这段时间再来补这个难的)

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

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

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

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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值