问题描述
小C面临一个选择问题:她可以从编号为1到n的糖果中任意选择一些糖果作为奖励,但需要遵守一个限制规则:如果选择了编号为i的糖果,那么编号为i-1、i-2、i+1、i+2的糖果将不能被选择。每个糖果都有一个对应的美味值,小C希望所选糖果的美味值之和最大。
例如:当有7个糖果,编号为1到7,美味值分别为 3, 1, 2, 7, 10, 2, 4,小C可以选择编号为1、4、7的糖果,这样可以得到最大美味值之和为 3 + 7 + 4 = 14。
解题思路
-
动态规划:
此题可以用动态规划的方法解决。动态规划的核心思想是将问题分解为子问题,并通过存储子问题的解来避免重复计算。
-
状态定义:
在这个问题中,可以定义一个状态
dp[i],表示在前i个糖果中,选择第i个糖果时的最大美味值之和。 -
状态转移方程:
- 如果选择了第
i个糖果,那么就不能选择第i-1、i-2、i+1、i+2个糖果。 - 因此,
dp[i]可以由dp[i-3]、dp[i-4]、dp[i-5]等状态转移而来。 - 具体的状态转移方程为:
dp[i] = max(dp[i-3], dp[i-4], dp[i-5], ...) + a[i]
- 如果选择了第
-
初始化:
对于前几个糖果,需要手动初始化
dp数组,因为它们没有足够的前置状态。 -
最终结果:
最终的最大美味值之和应该是
dp数组中的最大值。
代码实现
以下是上述解题思路的Java实现代码
public class Main {
public static int solution(int n, int[] a) {
// write code here
// 如果糖果数量小于等于2,直接返回最大值
if (n <= 2) {
return Math.max(a[0], a[1]);
}
// 定义dp数组,dp[i]表示选择第i个糖果时的最大美味值之和
int[] dp = new int[n];
// 初始化前几个糖果的dp值
dp[0] = a[0];
dp[1] = a[1];
dp[2] = a[2];
// 遍历每个糖果,计算dp值
for (int i = 3; i < n; i++) {
// 初始化dp[i]为a[i],因为至少要选择当前糖果
dp[i] = a[i];
// 从i-3到0遍历,找到最大值
for (int j = i - 3; j >= 0; j--) {
// 更新dp[i]
dp[i] = Math.max(dp[i], dp[j] + a[i]);
}
}
// 返回dp数组中的最大值
int maxSum = 0;
for (int i = 0; i < n; i++) {
maxSum = Math.max(maxSum, dp[i]);
}
return maxSum;
}
public static void main(String[] args) {
System.out.println(solution(7, new int[]{3, 1, 2, 7, 10, 2, 4}) == 14);
System.out.println(solution(5, new int[]{1, 10, 2, 5, 8}) == 18);
System.out.println(solution(6, new int[]{4, 5, 6, 1, 2, 3}) == 9);
}
}
代码中的关键点
- 初始化:前几个糖果的
dp值需要手动初始化。 - 状态转移:在遍历每个糖果时,从
i-3到0遍历,找到最大值并更新dp[i]。 - 最终结果:返回
dp数组中的最大值。
总结
在此题中,我们可以通过动态规划的方法逐步构建最优解。其中的运算关键在于定义好状态和状态转移方程,并正确初始化前几个糖果的状态。最终遍历 dp 数组,找到最大值即可。