这道题真正考的是:
动态规划(DP)
而且是非常经典的:
线性DP + 跳跃转移
一、题目讲了什么?
1、题目给了两个数组:
a[] b[]2、例如样例:
a: 1 2 3 4 b: 3 3 1 13、题目允许我们选择一些位置。
(1)假如选了位置 i:
获得:
a[i]分数。
(2)但是代价是:
会跳过后面若干位置。
(3)跳过多少?
由:
b[i]决定。
二、故事理解
1、想象有一条糖果街。
位置: 1 2 3 4 糖果: 1 2 3 42、每个位置还有一个魔法数字:
3 3 1 13、如果你站在第1格:
糖果=1 跳跃=3拿走糖果后:
获得1分然后必须跳到:
1+3=4位置。
4、如果站在第2格:
获得2分然后跳到:
2+3=5超出范围。
5、目标:
获得最多糖果三、DP状态定义
1、定义:
f[i]表示:
到达位置 i 时,之前已经获得的最大分数。
2、比如:
f[4]=10意思:
来到4号位置前 已经赚了10分3、注意:
还没拿4号位置的糖果。
四、两种选择
来到 i。
有两种情况。
选择1:拿糖果
获得:
a[i]分。
然后跳到:
i+b[i]位置。
于是:
f[i+b[i]] = max( f[i+b[i]], f[i]+a[i] )意思:
把当前糖果拿走 然后跳过去选择2:不拿糖果
直接走到下一格。
f[i+1] = max( f[i+1], f[i] )意思:
这格不要 继续往前五、手算样例
1、样例:
4 a: 1 2 3 4 b: 3 3 1 12、初始:
f: 0 0 0 0(1)i=1
拿:
得1分跳:
1+3=4所以:
f[4]=1不拿:
f[2]=0现在:
f: 0 0 0 1(2)i=2
拿:
2+3=5越界。
不能。
不拿:
f[3]=0(3)i=3
拿:
得3分跳:
3+1=4于是:
f[4] =max(1,3) =3(4)i=4
已有:
f[4]=3拿:
3+4 = 7(5)答案:
7六、为什么不是贪心
很多同学会想:
每次选最大的糖果例如:
10 1 1 100可能:
拿10后面就跳飞了。
失去:
100因此:
局部最优
≠
全局最优
所以必须DP。
七、参考代码
官方代码:
#include <cstdio> #include <algorithm> using namespace std; const int N = 100005; int n; int a[N]; int b[N]; long long f[N]; long long ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) { ans=max(ans,f[i]+a[i]); // 选当前位置 if(i+b[i]<=n) { f[i+b[i]] = max( f[i+b[i]], f[i]+a[i] ); } // 不选当前位置 f[i+1] = max( f[i+1], f[i] ); } printf("%lld\n",ans); return 0; }八、本题考点
1、本题经典模型:
走到位置 i ↓ 做决策 ↓ 跳到新位置 ↓ 继续DP2、结构:
dp[下一状态] = max( dp[下一状态], dp[当前状态]+收益 );九、记忆口诀
1、看到:
一条线 从左到右 每个位置有收益 选择后跳到新位置 求最大收益2、立刻想到:
线性DP 状态转移