学习目标:
- 掌握基础的动态规划状态机模型
- 掌握基础的压缩动态规划模型
- 掌握基础的区间动态规划模型
学习内容:
目录
状态机模型
什么是状态机?
1.有限状态机(Finite-state machine,FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
2.FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。
3.其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件
大盗阿福
题解:
这是一道经典的状态机问题,当小偷到达第i家店的时候,它与第i-1家是否被盗有关。如果说第i-1家被盗了,那么当前这一家就一定不能盗;如果说第i-1家没有被盗,那么第i家就可以盗也可以不盗。
在选择盗窃哪一个商店的时候,会有其中情况发生(盗窃的状态为1,不盗窃的状态为0).
情况一:0->0(表示不盗窃第i-1个商店,不盗窃第i个商店)
情况二:0->1(表示不盗窃第i-1个商店,盗窃第个商店)
情况三:1->0(表示盗窃第i-1个商店,不盗窃第i商店)
情况四:1->1(第i-1,和第i个商店都盗窃,这种情况是不存在的)
所以盗窃第i个物品对应的状态只有状态三
不盗窃第i个物品的状态为1,2 将这两个状态取最大值即可
//
// Created by 小魏 on 2023/6/1.
//
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=55;
int dp[N][2];//i表示盗窃第几家,后面表示状态 1 表示盗窃这一家店铺,0 表示不盗窃这一家店铺
int t,n;
int w[N];
int main(){
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
dp[1][1]=w[1];//进行初始化,表示拿第一个。如果不盗窃第一家店铺的话为dp[1][0]=0 因为初始化为0 所以可以省略
for(int i=2;i<=n ;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
dp[i][1]=dp[i-1][0]+w[i];
}
//因为不确定拿不拿最后一个,所以直接求最后一个拿与不拿的最大值
cout<<max(dp[n][0],dp[n][1])<<endl;
}
return 0;
}
//方法二:不用状态机
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N=1e5+10;
int dp[N];
int w[N];
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>w[i];
dp[1]=w[1];
for(int i=2;i<=n;i++)
dp[i]=max(dp[i-1],dp[i-2]+w[i]);
int cmax=0;
for(int i=1;i<=n;i++) cmax=max(camx,dp[i]);
cout<<cmax<<endl;
}
return 0;
}
股票购买Ⅳ
题解
这个也是一个状态机问题,可以将当前手中是否有股券作为不同的状态。但是题中说最多可以完成k笔交易。所以还要多一维。
三维,第一维表示是第几天个股票,第二维表示交易的数量,第三维是手里是否有股票
本题可以使用状态机来进行表达状态转移方程,其可以分成两个状态:
状态一:现在手里已经购买股票了,用dp[i][j][1]表示
状态二:现在手里没有购买彩票,用dp[i][j][0]表示
情况一:1->1 手里有股票而不卖出,对应方程为dp[i-1][j][1]
情况二:1->0 手里有股票然后卖出,对应方程为dp[i-1][j-1][1]-w[i]
情况三:0->1 手里没有股票,然后需要买彩票,对应状态转移方程为:dp[i-1][j][0]+w[i]
情况四:0->0 手里没有股票,也不买股票,对应状态转移方程为:dp[i-1][j][0]
//
// Created by 小魏 on 2023/6/1.
//
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1e5+10,M=110;
int dp[N][M][2];//三维 第一维表示第几天 第二维表示 当前为第几次完成交易 最后一维表示状态 1表示手中有 0表示手中无
int w[N];
int n,k;
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>w[i];
memset(dp,-0x3f,sizeof dp);//因为可能为负值,所以要进行初始化
for(int i=0;i<=n;i++) dp[i][0][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++){
dp[i][j][1]=max(dp[i-1][j-1][0]-w[i],dp[i-1][j][1]);
dp[i][j][0]=max(dp[i-1][j][1]+w[i],dp[i-1][j][0]);
}
//不确定那一天停手获得的前会更多,所以要一个一个进行尝试
int cmax=0;
for(int i=1;i<=k;i++){
cmax=max(cmax,dp[n][i][0]);
}
cout<<cmax<<endl;
return 0;
}
股票购买Ⅴ
思路分析:
这个因为加了一个冷冻期为一天:所以可以为分为三个状态:
状态一:手里有股票 dp[i][0]
状态二:手里无股票的第一天 dp[i][1]
状态三:手里无股票的>=2天 dp[i][2]
当手里有股票时,前一天有两种可能,有股票dp[i-1][0],手里无股票>=2天 dp[i-1][2]-w[i]
当手里无股票的第一天,前一天只有一种可能就是有股票 dp[i-1][0]+w[i]
当手里无股票的>=2天,前一天可能是手里无股票的>=2天 dp[i-1][2],也可能是手里无股票的第一天dp[i-1][1]
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+10,INF=0x3f;
int dp[N][3];
int w[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
dp[0][0]=dp[0][1]=-INF;
dp[0][2]=0;
for(int i=1;i<=n;i++)
{
//根据上面分析的状态,可以直接写状态转移方程
dp[i][0]=max(dp[i-1][0],dp[i-1][2]-w[i]);
dp[i][1]=dp[i-1][0]+w[i];
dp[i][2]=max(dp[i-1][2],dp[i-1][1]);
}
cout<<max(dp[n][1],dp[n][2])<<endl;
return 0;
}
状态压缩
蒙德里安的梦想
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=12,M=1<<12;
long long dp[N][M];
bool st[M];
int n,m;
int main()
{
while(true)
{
memset(dp,0,sizeof dp);
cin>>n>>m;
if(n==0&&m==0) break;
//先对st数组进行处理
for(int i=0;i<(1<<n);i++)
{
int cnt=0;
st[i]=true;
for(int j=0;j<n;j++)
if(i>>j&1)
{
if(cnt&1)
{
st[i]=false;
break;
}
}else cnt++;
if(cnt&1) st[i]=false;
}
//cout<<st[12]<<st[13]<<endl;
dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<(1<<n);j++)
for(int k=0;k<(1<<n);k++)
if((j&k)==0&&st[j|k])
dp[i][j]+=dp[i-1][k];
cout<<dp[m][0]<<endl;
}
return 0;
}
学习产出:
- CSDN 技术博客 10 篇
- 1-2篇为动态规划
- 3-5篇为宽搜和广搜
- 6-7篇为数据结构
- 8-9篇为图论
- 第10篇为数论和其他