视频讲解:GESP202312 六级 闯关游戏-信息学奥赛GESP等级考试真题解析
一、原题
题目描述
你来到了一个闯关游戏。
这个游戏总共有 N 关,每关都有 M 个通道,你需要选择一个通道并通往后续关卡。其中,第 i 个通道可以让你前进 ai 关,也就是说,如果你现在在第 x 关,那么选择第 i 个通道后,你将直接来到第 x+ai 关(特别地,如果 x+ai≥N,那么你就通关了)。此外,当你顺利离开第 s 关时,你还将获得 bs 分。
游戏开始时,你在第 0 关。请问,你通关时最多能获得多少总分。
输入格式
第一行两个整数 N,M,分别表示关卡数量和每关的通道数量。
接下来一行 M 个用单个空格隔开的整数 a0,a1⋯,aM−1。保证 1≤ai≤N。
接下来一行 N 个用单个空格隔开的整数 b0,b1⋯,bN−1。保证 ∣bi∣≤10^5。
输出格式
一行一个整数,表示你通关时最多能够获得的分数。
输入输出样例
输入 #1
bash
6 2
2 3
1 0 30 100 30 30
输出 #1
bash
131
输入 #2
bash
6 2
2 3
1 0 30 100 30 -1
输出 #2
bash
101
说明/提示
样例解释 1
你可以在第 0 关选择第 1 个通道,获得 1 分并来到第 3 关;随后再选择第 0 个通道,获得 100 分并来到第 5 关;最后任选一个通道,都可以获得 30 分并通关。如此,总得分为 1+100+30=131。
样例解释 2
请注意,一些关卡的得分可能是负数。
数据范围
对于 20% 的测试点,保证 M=1。
对于 40% 的测试点,保证 N≤20;保证 M≤2。
对于所有测试点,保证 1≤N≤10^4;保证 1≤M≤100。
二、做题思路
1)填充数据

cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//1)填充数据
//1.1)确定关卡数量n 通道数量m
int n,m;cin>>n>>m;
//1.2)填充m个通道 排序
int a[110]={};
for(int i=1;i<=m;i++) cin>>a[i];
sort(a+1,a+m+1);
//1.3)填充0-n-1个关卡的分数
int b[10005]={};
for(int i=0;i<=n-1;i++) cin>>b[i];
return 0;
}
2)动态规划:求出当前格子的最大分数
2.1)dp初始化

cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//1)填充数据
//...
//2)动态规划:求出当前格子的最大分数
//2.1)dp初始化
vector<int> dp(10005,-1e9);
dp[0]=b[0];
return 0;
}
2.2)从最小通道的关卡到n-1关

cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//1)填充数据
//...
//2)动态规划:求出当前格子的最大分数
//2.1)dp初始化
vector<int> dp(10005,-1e9);
dp[0]=b[0];
//2.2)从最小通道的关卡到n-1关
for(int i=a[i];i<=n-1;i++){
}
return 0;
}
2.3)循环每个通道(判断能否到达)

cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//1)填充数据
//...
//2)动态规划:求出当前格子的最大分数
//2.1)dp初始化
vector<int> dp(10005,-1e9);
dp[0]=b[0];
//2.2)从最小通道的关卡到n-1关
for(int i=a[i];i<=n-1;i++){
//2.3)循环每个通道(判断能否到达)
for(int j=1;j<=m;j++){
if(dp[i-a[j]]!=-1e9&&i-a[j]>=0){
dp[i]=max(dp[i],dp[i-a[j]]+b[i]);
}
}
}
return 0;
}
2.4)循环最大通道前的分数,找最大值
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//1)填充数据
//...
//2)动态规划:求出当前格子的最大分数
//2.1)dp初始化
vector<int> dp(10005,-1e9);
dp[0]=b[0];
//2.2)从最小通道的关卡到n-1关
for(int i=a[i];i<=n-1;i++){
//2.3)循环每个通道(判断能否到达)
for(int j=1;j<=m;j++){
if(dp[i-a[j]]!=-1e9&&i-a[j]>=0){
dp[i]=max(dp[i],dp[i-a[j]]+b[i]);
}
}
}
//2.4)循环最大通道前的分数,找最大值
int ans=INT_MIN;
for(int i=n-a[m];i<=n-1;i++){
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
三、答案
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//1)填充数据
//1.1)确定关卡数量n 通道数量m
int n,m;cin>>n>>m;
//1.2)填充m个通道 排序
int a[110]={};
for(int i=1;i<=m;i++) cin>>a[i];
sort(a+1,a+m+1);
//1.3)填充0-n-1个关卡的分数
int b[10005]={};
for(int i=0;i<=n-1;i++) cin>>b[i];
//2)动态规划:求出当前格子的最大分数
//2.1)dp初始化
vector<int> dp(10005,-1e9);
dp[0]=b[0];
//2.2)从最小通道的关卡到n-1关
for(int i=a[i];i<=n-1;i++){
//2.3)循环每个通道(判断能否到达)
for(int j=1;j<=m;j++){
if(dp[i-a[j]]!=-1e9&&i-a[j]>=0){
dp[i]=max(dp[i],dp[i-a[j]]+b[i]);
}
}
}
//2.4)循环最大通道前的分数,找最大值
int ans=INT_MIN;
for(int i=n-a[m];i<=n-1;i++){
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
