题目描述
本题是一个典型的动态规划问题,要求在一组观光景点中找到一对景点,使得观光组合得分最高。观光组合得分定义为:values[i] + values[j] + i - j,其中 values[i] 和 values[j] 分别表示第 i 个和第 j 个景点的评分,i 和 j 分别表示景点的下标。题目要求 i < j,即第一个景点的下标必须小于第二个景点的下标。
解题思路
为了求解这个问题,我们可以使用动态规划(Dynamic Programming,简称 DP)的方法。动态规划是一种将复杂问题分解成简单子问题求解的算法设计技术,它将子问题的解存储起来,避免重复计算,从而提高算法的效率。
我们可以定义一个一维数组 dp,其中 dp[i] 表示当选第 i 个景点作为第一个景点时,第二个景点选哪个可以使得分最高。具体步骤如下:
-
初始化
dp数组,dp[n - 2] = n - 1,因为当只有一个景点可选时,只能选择最后一个景点。 -
从后往前遍历景点,对于每个景点
i,计算两个得分:score1:选择j = dp[i + 1]作为第二个景点时的得分。score2:选择j = i + 1作为第二个景点时的得分。
-
比较两个得分,将较大的得分对应的景点下标赋值给
dp[i]。可以这样做的原因是:如果选第i个景点作为第一个景点时,如果目标的第二个景点下标的j在i + 2 <= j < n的范围中,若要使values[i] + values[j] + i - j最大,即values[j] - j最大,则目标的j应该也能使values[i + 1] + values[j] + i + 1 - j最大,因此j就是当选择第i + 1个景点作为第一个景点时所选择的能使最终得分最高的景点,即j = dp[i + 1]。如果第二个景点的下标j在i + 1 <= j < i + 2的范围中,则只能j = i + 1。因此,只要根据j = dp[i + 1]和j = i + 1两种情况,计算并比较最终的得分哪个更大,将dp[i]设为对应的j即可。 -
遍历完成后,
dp数组中的每个元素都表示了当选第i个景点作为第一个景点时,第二个景点的最优选择。 -
再次遍历
dp数组,计算所有可能的观光组合得分,并找出最大值。
代码实现
以下是题目给出的正确代码:
public class Main {
public static int solution(int[] values) {
if (values.length <= 1) {
return 0;
}
int n = values.length;
// dp[i] = j 表示:当选第i个景点作为第一个景点时,第二个景点选第j个景点可以使得分最高
int[] dp = new int[n];
dp[n - 2] = n - 1;
for (int i = n - 3; i >= 0; i--) {
int lastJ = dp[i + 1];
int score1 = values[lastJ] + i - lastJ;
int score2 = values[i + 1] - 1;
if (score1 > score2) {
dp[i] = lastJ;
} else {
dp[i] = i + 1;
}
}
int ret = Integer.MIN_VALUE;
for (int i = 0; i < n - 1; i++) {
int j = dp[i];
ret = Math.max(ret, values[i] + values[j] + i - j);
}
return ret;
}
public static void main(String[] args) {
System.out.println(solution(new int[]{8, 3, 5, 5, 6}) == 11 ? 1 : 0);
System.out.println(solution(new int[]{10, 4, 8, 7}) == 16 ? 1 : 0);
System.out.println(solution(new int[]{1, 2, 3, 4, 5}) == 8 ? 1 : 0);
}
}
时间复杂度和空间复杂度
- 时间复杂度:
O(n),其中n为景点数组的长度。我们需要遍历两次数组,第一次是初始化dp数组,第二次是计算最大得分,因此时间复杂度为O(n)。 - 空间复杂度:
O(n),我们使用了一个长度为n的一维数组dp来存储中间结果。