线性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);
*/