题解:CF358D Dima and Hares
CF358D 题解
题面
思路
挺好的一道 DP 题。
首先分析题目会发现这道题的 DP 有点特殊:每一个点的状态会与前后两道题的状态相互影响与改变。
而观察到,一个点 \(i\) 的状态均可以表示成如下两种情况。
- 先选第 \(i+1\) 个物品,再取第 \(i\) 个物品。(取物品的操作并不一定是相邻的,只是第 \(i\) 个物品在取了第 \(i+1\) 个物品后取的。)
- 先选第 \(i\) 个物品,再选第 \(i+1\) 个物品。(取物品的操作并不一定是相邻的,只是第 \(i+1\) 个物品在取了第 \(i\) 个物品后取的。)
于是很容易想到可以设一个二维的 \(dp\) 数组:\(dp[i][2]\)。
- \(dp[i][0]\) 表示先取第 \(i+1\) 个物品获得的前 \(i\) 个物品的最大值。
- \(dp[i][1]\) 表示后取第 \(i+1\) 个物品获得的前 \(i\) 个物品的最大值。
接下来就开始推递推方程。
对于一个 \(dp[i][0]\),是先取第 \(i+1\) 个物品,后取第 \(i\) 个物品,这个时候只要讨论 \(dp[i-1]\) 的两种状态即可。
- 对于 \(dp[i-1][0]\) 是先取第 \(i\) 个物品,后取第 \(i-1\) 个物品,此时取第 \(i\) 个物品的时候,相邻的两个物品有一个被拿过,所以得到的价值为 \(b[i]\)。
- 对于 \(dp[i-1][1]\) 是先取第 \(i-1\) 个物品,后取第 \(i\) 哥物品,此时取第 \(i\) 个物品的时候,相邻的两个物品都被拿过,所以得到的价值为 \(c[i]\)。
因此 \(dp[i][0]=\max(dp[i-1][0]+b[i],dp[i-1][1]+c[i])\)。
对于一个 \(dp[i][1]\),是先取第 \(i\) 个物品,后取第 \(i+1\) 个物品,这个时候一样的讨论 \(dp[i-1]\) 的两种状态即可。
- 对于 \(dp[i-1][0]\) 是先取第 \(i\) 个物品,后取第 \(i-1\) 个物品,此时取第 \(i\) 个物品的时候,相邻的两个物品都没有被拿过,所以得到的价值为 \(a[i]\)。
- 对于 \(dp[i-1][1]\) 是先取第 \(i-1\) 个物品,后取第 \(i\) 哥物品,此时取第 \(i\) 个物品的时候,相邻的两个物品有一个被拿过,所以得到的价值为 \(b[i]\)。
因此 \(dp[i][1]=\max(dp[i-1][0]+a[i],dp[i-1][1]+b[i])\)。
故我们就可以线性求出 \(dp\) 数组了。
接下来,题目询问获得的最大价值,注意到第 \(n\) 个物品只可能是先取第 \(n\) 个物品,后取第 \(n+1\) 个物品,因为不存在第 \(n+1\) 个物品。
故答案为 \(dp[n][1]\)。
注意,一开始要初始化,给 \(dp\) 赋值成一个极小值(我因为没做这个,WA 过。)。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define INF 0x7fffffff/2
using namespace std;
const int MN=3005;
long long n,a[MN],b[MN],c[MN],dp[MN][2];
//dp[i][0] 表示先取第 i+1 个物品获得的前 i 个物品的最大值。
//dp[i][1] 表示后取第 i+1 个物品获得的前 i 个物品的最大值。
int main(){
scanf("%lld",&n);
for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
for(int i=1; i<=n; i++) scanf("%lld",&b[i]);
for(int i=1; i<=n; i++) scanf("%lld",&c[i]);
for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) dp[i][j]=-INF;
dp[0][0]=0;
for(int i=1; i<=n; i++){
dp[i][0]=max(dp[i-1][0]+b[i],dp[i-1][1]+c[i]);
dp[i][1]=max(dp[i-1][0]+a[i],dp[i-1][1]+b[i]);
}
printf("%lld",dp[n][1]);
return 0;
}