线性dp,所谓线性dp,就是指我们的递归方程有一个明显的线性关系的,有可能是一维线性的,也可能是二维线性的.
阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。
这条街上一共有 NN 家店铺,每家店中都有一些现金。阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。
作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?
输入格式
输入的第一行是一个整数 T (T≤50) ,表示一共有 TT 组数据。
接下来的每组数据,第一行是一个整数 N (1≤N≤100000),表示一共有 N 家店铺。
第二行是 N 个被空格分开的正整数,表示每一家店铺中的现金数量。每家店铺中的现金数量均不超过 1000。
输出格式
对于每组数据,输出一行。
该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。
提示
对于第一组样例,阿福选择第 2家店铺行窃,获得的现金数量为 8。对于第二组样例,阿福选择第 1 和 4 家店铺行窃,获得的现金数量为 10 + 14 = 24。
样例输入
2
3
1 8 2
4
10 7 6 14
样例输出
8
24
1.2.1 状态表示:
f[i] 表示偷前i家店铺能获取的最大值。
1.2.2 状态转移:
状态转移可以用带权的有向图表示,点表示状态,权表示转移的价值增量。
转移的情况:
1、不选第 i 家店铺,那么就可以选择第 i-1家店铺,所以f[i]=f[i-1]
2、选第 i 家店铺,那么就不能选择第 i-1 家店铺,只能选择第 i-2 家店铺,因为会选择第 i 家店铺,所以f[i]=f[i-2]+w[i]
1.2.3 边界情况:
根据题意即可知边界为:
1.2.4 完整代码:
#include
using namespace std;
const int N=500010;
int f[N];
int w[N];
int main ()
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++){
cin>>m;
for(int i=1;i<=m;i++)
cin>>w[i];
//dp
f[1]=w[1];//边界条件
for(int i=2;i<=m;i++)
f[i]=max(f[i-1],f[i-2]+w[i]);
cout<<f[m]<<endl;
}
return 0;
}
1.3.1 状态表示:
f[i][0] 表示不偷第 i 家店铺能获得的最大值
f[i][1] 表示偷第 i 家店铺能获得的最大值
1.3.2 状态转移:
转移递推式:
不偷:f[i][0]=max( f[i-1][0] , f[i-1][1])
偷:f[i][1]=f[i-1][0] + w[i]
1.3.3 边界情况:
根据题意可知边界为:
// 时间:2022.09.07 18点09分
// 算法:线性DP
#include
using namespace std;
const int N=510;
int f[N][N];
int w[N];
int main ()
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++){
cin>>m;
for(int i=1;i<=m;i++)
cin>>w[i];
//dp
f[1][0]=0,f[1][1]=w[1];
for(int i=2;i<=m;i++){
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+w[i];
}
cout<<max(f[m][0],f[m][1])<<endl;
}
return 0;
}
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
2.2.1 状态表示:
2.2.3 边界情况:
// 时间:2022.09.07 19点56分
// 算法:线性DP
#include
using namespace std;
const int N=510;
int f[N][N];
int w[N];
int main ()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>w[i];
f[1][0]=0,f[1][1]= -w[1];
//dp
for(int i=2;i<=n;i++){
f[i][0]=max(f[i-1][0],f[i-1][1]+w[i]);
f[i][1]=max(f[i-1][1],f[i-1][0]-w[i]);
}
cout<<f[n][0]<<endl;
return 0;
}
题目链接
3.2.1 状态表示:
3.2.2 状态转移:
带权的有向图,点表示状态,边权表示转移的价值增量。
状态递推式:
3.2.3 边界情况:
(i:1 ~ n; j: 1 ~ k)
第0天:f(0,j,0)=0
f(0,j,1)=-1e6
第0笔:f(i,0,0)=0
边界赋值技巧:
合法则赋可取的有限值
非法则赋负无穷或正无穷
#include
using namespace std;
const int N=100;
int f[N][N][N];
int w[110];
int main ()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>w[i];
//边界情况
for(int i=0;i<=k;i++) f[0][i][1]=-1e6;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;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]);
}
cout<<f[n][k][0]<<endl;
return 0;
}
题目链接
// 算法:动态规划之线性DP
// 时间:2022.07.13 15点45分
#include
#include
using namespace std;
const int N=510;
int n,INF=1e9;
int a[N][N],f[N][N];
int main ()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>a[i][j];
for(int i=0;i<=n;i++)
for(int j=0;j<=i+1;j++)
//注意此时初始化需要多初始化一位,因为算边界时会算右上角的最大值,所以把最上角初始化为负无穷
f[i][j]=-INF;
f[1][1]=a[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
//遍历最后一行的最大值
int res=-INF;
for(int i=1;i<=n;i++) res=max(res,f[n][i]);
cout<<res<<endl;
return 0;
}