大盗阿福 线性DP/状态机

活动 - AcWing

线性dp:(直接看每次状态的结果)
就是走到第i个商铺的时候,可以打劫可以不选择:(看着和背包有点像,但其实是这样的思考方式)
令f[i]为走到第i家的最大价值
不选:那第i家的最大价值就是f[i-1];
选:那第i-1家不可能选的,状态转移得从f[i-2]转移过来,f[i-2]+a[i];
所以两者取最大值就好
有人说这样看着比较乱,我觉得还好,因为话粗理不粗,确实是f[i-1]或者f[i-2]+a[i]转移过来
不用管之前的选不选,因为每一层的状态转移都是一样的
所以这个是从结果来考虑的状态转移方程式

#include<iostream>
#include<cstring>
using namespace std;
const int N=2e6+10;
int a[N],f[N];
int main(){
    int T;
    cin>>T;
    while(T--)
    {
        memset(f,0,sizeof(f));
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        f[1]=a[1];
        for(int i=2;i<=n;i++)
        {
            f[i]=max(f[i-2]+a[i],f[i-1]);
        }
        cout<<f[n]<<"\n";
    }
    return 0;
}

状态机:
刚刚是从结果入手,也可以从过程的状态来入手
令f[i][j]为走到第i家商铺,且第i家商铺的状态是j(这个题用0和1表示,1表示选,0反之)
f[i][0]:可以从上一层的两种情况转移过来:
第一种是上一层本来就不选:f[i-1][0]
第二种是上一层选了这一层不选,那最大价值:f[i-1][1]
f[i][1]:表示选择第i个物品
那只能从上一层的不选的状态转移过来f[i-1][1]
然后到最后的第n家,可以选也可以不选,所以取两种状态的最大值就好

#include<iostream>
using namespace std;
const int N=2e5+10,INF=0x3f3f3f3f;
int f[N][2],w[N];
int main(){
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        f[0][0]=0,f[0][1]=-INF;//f[0][1]不能取得,所以初始化为负无穷,就不会从这个状态转移过去了
        for(int i=1;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][1],f[n][0])<<"\n";
    }
    return 0;
}

来个例题:D-Breezing_牛客小白月赛53 (nowcoder.com)

这题我原本打算构造写的,但并不是相邻,所以很容易推翻

然后根据贪心策略可得,每一个位置的数要么取1,要么不变,一共两种状态,可以用状态机

令f[i][0/1]为到i时的状态是0(变成1),或者1(本身)

状态转移方程:

f[i][0]=max(f[i-1][1]+abs(a[i-1]-1),f[i-1][0]);
f[i][1]=max(f[i-1][1]+abs(a[i]-a[i-1]),f[i-1][0]+abs(a[i]-1);

然后代码就好写了

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define IOS ios::sync_with_stdio(false), cin.tie(0);
#include<iostream>
#include<map>
#include<set>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
using namespace std;
//#define int long long 
typedef long long ll;
typedef pair<int,int> PAII;
const int N=2e6+10,M=5050,INF=0x3f3f3f3f,mod=998244353;
int a[N];
int f[N][2];
signed main(){
    //IOS; 
    int T;
    T=1;
    //cin>>T;
    while(T--)
    {   
    	int n;
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		f[1][0]=0;
		f[1][1]=0;
		for(int i=2;i<=n;i++)
		{
			f[i][0]=max(f[i-1][1]+abs(a[i-1]-1),f[i-1][0]);
			f[i][1]=max(f[i-1][1]+abs(a[i]-a[i-1]),f[i-1][0]+abs(a[i]-1));
		}
		cout<<max(f[n][1],f[n][0])<<"\n";
    }
    return 0;
}
/*
每个数只有两个状态
0:取1
1:取max
f[i][0/1]
f[i][0]=max(f[i-1][1]+abs(a[i-1]-1),f[i-1][0]);
f[i][1]=max(f[i-1][1]+abs(a[i]-a[i-1]),f[i-1][0]+abs(a[i]-1); 
*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值