题目描述
你来到了一个闯关游戏。
这个游戏总共有 N N N 关,每关都有 M M M 个通道,你需要选择一个通道并通往后续关卡。其中,第 i i i 个通道可以让你前进 a i a_i ai 关,也就是说,如果你现在在第 x x x 关,那么选择第 i i i 个通道后,你将直接来到第 x + a i x+a_i x+ai 关(特别地,如果 x + a i ≥ N x + a_i \geq N x+ai≥N,那么你就通关了)。此外,当你顺利离开第 s s s 关时,你还将获得 b s b_s bs 分。
游戏开始时,你在第 0 0 0 关。请问,你通关时最多能获得多少总分。
输入格式
第一行两个整数 N N N, M M M,分别表示关卡数量和每关的通道数量。
接下来一行 M M M 个用单个空格隔开的整数 a 0 , a 1 ⋯ , a M − 1 a_0,a_1\cdots,a_{M-1} a0,a1⋯,aM−1。保证 1 ≤ a i ≤ N 1\le a_i \le N 1≤ai≤N。
接下来一行 N N N 个用单个空格隔开的整数 b 0 , b 1 ⋯ , b N − 1 b_0,b_1\cdots,b_{N-1} b0,b1⋯,bN−1。保证 ∣ b i ∣ ≤ 1 0 5 |b_i|\le 10^5 ∣bi∣≤105。
输出格式
一行一个整数,表示你通关时最多能够获得的分数。
输入输出样例 #1
输入 #1
6 2
2 3
1 0 30 100 30 30
输出 #1
131
输入输出样例 #2
输入 #2
6 2
2 3
1 0 30 100 30 -1
输出 #2
101
说明/提示
样例解释 1
你可以在第 0 0 0 关选择第 1 1 1 个通道,获得 1 1 1 分并来到第 3 3 3 关;随后再选择第 0 0 0 个通道,获得 100 100 100 分并来到第 5 5 5 关;最后任选一个通道,都可以获得 30 30 30 分并通关。如此,总得分为 1 + 100 + 30 = 131 1+100+30=131 1+100+30=131。
样例解释 2
请注意,一些关卡的得分可能是负数。
数据范围
对于 20 % 20\% 20% 的测试点,保证 M = 1 M=1 M=1。
对于 40 % 40\% 40% 的测试点,保证 N ≤ 20 N \le 20 N≤20;保证 M ≤ 2 M\le 2 M≤2。
对于所有测试点,保证 1 ≤ N ≤ 1 0 4 1 \le N \le 10^4 1≤N≤104;保证 1 ≤ M ≤ 100 1 \le M\le 100 1≤M≤100。
提交链接
闯关游戏
解析
搜索的想法(40分)
深度优先搜索(DFS) 来枚举所有可能的路径,以找到通关时的最高得分。
主要逻辑
dfs(pos, score)
:
pos
代表当前关卡编号score
代表目前的累计得分- 递归终止条件:当
pos >= n
(即通关)时,更新mx
记录的最大得分。 - 否则,尝试
m
种通道,每种都递归调用dfs(pos + a[i], score + b[pos])
。
main()
:
- 读取输入 N N N, M M M(关卡数和通道数)。
- 读取每个通道的跳跃距离 a [ i ] a[i] a[i]。
- 读取每一关的得分 b [ i ] b[i] b[i]。
- 调用
dfs(0, 0)
从0
关开始搜索所有可能的通关路径。
时间复杂度分析
最坏情况分析:在最坏情况下,DFS 会搜索所有可能的通关路径。
状态树的深度
- 最深可能达到 N N N(最坏情况下一次只前进 1 1 1 关)。
每层的分支数
- 每一层最多有 M M M 个选择(每一关有 M M M 个通道)。
搜索空间
- 递归构成了一棵 最大深度 N N N,每个节点最多 M M M 个子节点 的搜索树。
- 这样搜索树的节点数最多是 M N M^N MN(指数级别)。
对于 40 % 40\% 40% 的测试点,保证 N ≤ 20 N \le 20 N≤20;保证 M ≤ 2 M\le 2 M≤2,时间复杂度可以承受。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e4 + 9;int n, m, a[109], b[MAX_N], mx = -1e9; //mx初值为一个比较小的数(分数有负数存在)int cost[10009];void dfs(int pos, int score)
{if (pos >= n){mx = max(mx, score);return;}for (int i = 0; i < m; i++)dfs(pos + a[i], score + b[pos]);
}
int main()
{cin >> n >> m; // 关卡的数量 每一关的通道数量for (int i = 0; i < m; i++)cin >> a[i]; // 第i个通道前进a[i]关for (int i = 0; i < n; i++)cin >> b[i]; // 通过第i关时获得的分数为b[i]dfs(0, 0);cout << mx;return 0;
}
动态规划的想法(100分)
动态规划(DP) 来代替深度优先搜索。动态规划能避免递归树的指数级别增长,优化到 O ( N ∗ M ) O(N*M) O(N∗M) 的复杂度。
使用一个一维 d p dp dp 数组来表示从第 x x x 关卡到达时的最大得分。然后通过迭代的方式,更新每个关卡的得分。
动态规划思路:
状态定义:
- d p [ x ] dp[x] dp[x] 表示到达 x x x 关时的最大得分。
状态转移:
- 从关卡 x x x 选择任意通道 i i i,到达 x + a [ i ] x + a[i] x+a[i] 关,得分更新为 d p [ x + a [ i ] ] = m a x ( d p [ x + a [ i ] ] , d p [ x ] + b [ x ] ) dp[x + a[i]] = max(dp[x + a[i]], dp[x] + b[x]) dp[x+a[i]]=max(dp[x+a[i]],dp[x]+b[x])。
初始化:
- d p [ 0 ] = 0 dp[0] =0 dp[0]=0(从第 0 0 0 关开始)。
目标:
- 最终我们希望到达的关卡 ≥ n ≥n ≥n,表示最后能通关时的最高得分。每一个 a [ i ] a[i] a[i] 最大为 n n n,我们可以求 d p [ n ] ∼ d p [ 2 ∗ n ] dp[n] \sim dp[2*n] dp[n]∼dp[2∗n] 的最大值即可。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e4 + 9;int n, m, a[109], b[MAX_N], mx = -1e9;
int dp[2 * MAX_N]; // dp[i]:到达第i个关卡的最大分数int main()
{cin >> n >> m; // 关卡的数量 每一关的通道数量for (int i = 1; i <= 2 * n; i++)dp[i] = -1e9;for (int i = 0; i < m; i++)cin >> a[i]; // 第i个通道前进a[i]关for (int i = 0; i < n; i++)cin >> b[i]; // 通过第i关时获得的分数为b[i]for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)dp[i + a[j]] = max(dp[i + a[j]], dp[i] + b[i]);for (int i = n; i <= 2 * n; i++)mx = max(mx, dp[i]);cout << mx;return 0;
}