观光景点组合得分问题笔记

94 阅读4分钟

问题描述

给定一个数组 values,数组中的每个元素表示一个观光景点的评分。景点之间的距离由它们的下标差值 j - i 表示(其中 ij 是景点的下标,且 i < j)。对于一对景点 (i, j),我们定义它们的观光组合得分为:

css
复制代码
得分 = values[i] + values[j] + i - j

其中:

  • values[i]values[j] 分别是第 i 个和第 j 个景点的评分。
  • i - j 表示它们之间的下标差。

小R的目标是找到一对景点 (i, j),使得 i < j,并且组合得分最大。

输入与输出

  • 输入:一个整数数组 values,其中 values[i] 表示第 i 个景点的评分。
  • 输出:一个整数,表示能够获得的最大观光组合得分。

示例

样例1

css
复制代码
输入:values = [8, 3, 5, 5, 6]
输出:11

样例2

css
复制代码
输入:values = [10, 4, 8, 7]
输出:16

样例3

css
复制代码
输入:values = [1, 2, 3, 4, 5]
输出:8

思路分析

我们需要最大化表达式:

css
复制代码
得分 = values[i] + values[j] + i - j

其中 i < j

重写得分公式

将得分公式进行变形,得分为:

css
复制代码
得分 = (values[i] + i) + (values[j] - j)

这意味着:

  • 对于 i 索引的景点,计算 values[i] + i
  • 对于 j 索引的景点,计算 values[j] - j

因此,问题转化为:我们要找到一对 ij,使得 i < jvalues[i] + ivalues[j] - j 的和最大。

最大化得分

从上面的公式我们可以得出:

  • 对于任意一个 j,我们可以计算 values[j] - j 的值。
  • 然后,我们通过遍历所有的 i,计算出 values[i] + i 的值,尽量寻找最大值。
  • 对于每个 j,最大化 values[i] + i + values[j] - j 的值。这个可以通过在遍历过程中保持 values[i] + i 的最大值来实现。

具体步骤

  1. 初始化一个变量 max_i = values[0] + 0,表示 values[i] + i 的最大值。
  2. 遍历数组中的每一个 j,计算出 values[j] - j,然后将 max_ivalues[j] - j 相加,得到当前的得分。
  3. 更新 max_ivalues[j] + j 中的最大值,继续向下遍历。

时间复杂度

  • 我们只需要遍历一次数组,因此时间复杂度为 O(n),其中 n 是数组的长度。

java代码实现

public class MaxScenicScore {
    public static int maxScoreSightseeingPair(int[] values) {
        // 初始化 max_i 为第一个景点的得分
        int max_i = values[0] + 0;
        int maxScore = Integer.MIN_VALUE;

        // 从第二个景点开始遍历
        for (int j = 1; j < values.length; j++) {
            // 计算当前的得分
            maxScore = Math.max(maxScore, max_i + values[j] - j);

            // 更新 max_i,表示从 i 到 j 之间所有景点中的最大值
            max_i = Math.max(max_i, values[j] + j);
        }

        return maxScore;
    }

    public static void main(String[] args) {
        // 示例1
        int[] values1 = {8, 3, 5, 5, 6};
        System.out.println("最大得分:" + maxScoreSightseeingPair(values1));  // 输出:11

        // 示例2
        int[] values2 = {10, 4, 8, 7};
        System.out.println("最大得分:" + maxScoreSightseeingPair(values2));  // 输出:16

        // 示例3
        int[] values3 = {1, 2, 3, 4, 5};
        System.out.println("最大得分:" + maxScoreSightseeingPair(values3));  // 输出:8
    }
}

代码解释

  1. maxScoreSightseeingPair 方法:这是我们求解最大得分的核心方法。

    • max_i 初始化为第一个景点的得分(即 values[0] + 0)。
    • 然后,我们开始从第二个景点 j = 1 开始遍历。对于每一个景点 j,我们计算出 values[j] - j,并将其与 max_i 相加,得到当前的得分。
    • 更新 max_i,它存储的是当前为止所有景点中 values[i] + i 的最大值。
  2. main 方法:这里用来测试函数,输出三个示例的最大得分。

测试案例分析

  1. 样例1: 输入:values = [8, 3, 5, 5, 6]

    • 初始时,max_i = 8 + 0 = 8
    • 对于 j = 1values[1] - 1 = 3 - 1 = 2,当前得分 8 + 2 = 10
    • 对于 j = 2values[2] - 2 = 5 - 2 = 3,当前得分 8 + 3 = 11
    • 对于 j = 3values[3] - 3 = 5 - 3 = 2,当前得分 8 + 2 = 10
    • 对于 j = 4values[4] - 4 = 6 - 4 = 2,当前得分 8 + 2 = 10
    • 最大得分为 11。
  2. 样例2: 输入:values = [10, 4, 8, 7]

    • 初始时,max_i = 10 + 0 = 10
    • 对于 j = 1values[1] - 1 = 4 - 1 = 3,当前得分 10 + 3 = 13
    • 对于 j = 2values[2] - 2 = 8 - 2 = 6,当前得分 10 + 6 = 16
    • 对于 j = 3values[3] - 3 = 7 - 3 = 4,当前得分 10 + 4 = 14
    • 最大得分为 16。
  3. 样例3: 输入:values = [1, 2, 3, 4, 5]

    • 初始时,max_i = 1 + 0 = 1
    • 对于 j = 1values[1] - 1 = 2 - 1 = 1,当前得分 1 + 1 = 2
    • 对于 j = 2values[2] - 2 = 3 - 2 = 1,当前得分 1 + 1 = 2
    • 对于 j = 3values[3] - 3 = 4 - 3 = 1,当前得分 1 + 1 = 2
    • 对于 j = 4values[4] - 4 = 5 - 4 = 1,当前得分 1 + 1 = 2
    • 最大得分为 8。

总结

通过分析问题,我们发现可以将问题转化为一个较为简单的求最大值的问题。在代码中,我们通过一次遍历计算出每一对 (i, j) 的得分,并在过程中更新最大值。最终得到了高效的解法,时间复杂度为 O(n)