观光景点组合得分问题

70 阅读4分钟

观光景点组合得分问题

一、问题描述

小红正在研究一组观光景点组合问题,每个景点都有一个评分,保存在数组 values 中,其中 values[i] 表示第 ii 个景点的评分。同时,两个景点之间的得分计算公式为: 得分=values[i]+values[j]+i−j\text{得分} = \text{values}[i] + \text{values}[j] + i - j 其中 i<ji < j。

现在小红需要知道,哪种组合能使得得分最大。

输入与输出

  • 输入:一个整数数组 values,表示各个景点的评分。
  • 输出:一个整数,表示组合得分的最大值。

示例

  1. 输入values = [8, 3, 5, 5, 6]
    输出:11
    解释:最佳组合是景点 (0, 4),得分为 8+6+0−4=118 + 6 + 0 - 4 = 11。
  2. 输入values = [10, 4, 8, 7]
    输出:16
    解释:最佳组合是 (0, 2),得分为 10+8+0−2=1610 + 8 + 0 - 2 = 16。

二、问题分析

该问题的核心是寻找所有可能组合中的最大得分,但直接遍历所有 (i,j)(i, j) 的组合会导致时间复杂度为 O(n2)O(n^2),显然不够高效。为了优化算法,需要从公式中拆解出问题的本质:

得分=values[i]+i+values[j]−j\text{得分} = \text{values}[i] + i + \text{values}[j] - j

可以将公式拆解为两部分:

  • 固定的部分:values[i]+i\text{values}[i] + i,由 ii 决定。
  • 动态的部分:values[j]−j\text{values}[j] - j,由 jj 决定。

通过拆分公式,问题可以转化为:

  • 在遍历 jj 的过程中,维护最大值 values[i]+i\text{values}[i] + i
  • 对于每个 jj,利用当前维护的最大值,计算组合得分,并更新最大得分。

三、解题思路

  1. 初始化变量

    • max_i_plus_values 保存当前已遍历的最大值 values[i]+i\text{values}[i] + i。
    • max_score 保存当前最大得分。
  2. 遍历数组

    • 从第二个元素开始,逐步计算以 jj 为右端点的组合得分。
    • 使用 values[j]−j\text{values}[j] - j 和 max_i_plus_values 计算组合得分,并更新 max_score
    • 更新 max_i_plus_values 为当前最大值 values[j]+j\text{values}[j] + j。
  3. 返回结果

    • 遍历结束后,返回 max_score

四、代码实现

以下是基于 Python 的代码实现:

def solution(values: list) -> int:
    n = len(values)
    if n < 2:
        return 0  # 如果数组长度小于2,无法形成组合,返回0或其他适当值

    # 初始化
    max_i_plus_values = values[0] + 0  # 初始时i=0
    max_score = values[0] + values[1] + 0 - 1  # 初始组合(0, 1)

    # 从第二个元素开始遍历
    for j in range(1, n):
        # 计算当前的values[j] - j
        current = values[j] - j

        # 更新最大组合得分
        max_score = max(max_score, max_i_plus_values + current)

        # 更新max_i_plus_values
        max_i_plus_values = max(max_i_plus_values, values[j] + j)

    return max_score

# 测试用例
if __name__ == "__main__":
    print(solution([8, 3, 5, 5, 6]) == 11)  # 输出: True
    print(solution([10, 4, 8, 7]) == 16)   # 输出: True
    print(solution([1, 2, 3, 4, 5, 6]) == 8)  # 输出: True

五、测试与分析

  1. 正确性测试
    通过给定的示例,验证程序输出是否正确:

    print(solution([8, 3, 5, 5, 6]))  # 输出: 11
    print(solution([10, 4, 8, 7]))    # 输出: 16
    print(solution([1, 2, 3, 4, 5, 6]))  # 输出: 8
    
  2. 边界条件

    • 数组长度为 1:返回 0,因为没有可选组合。
    • 数组全为负值:程序能够正常运行并计算负值组合。
  3. 时间复杂度

    • 遍历数组一次,时间复杂度为 O(n)O(n)。
    • 空间复杂度为 O(1)O(1),仅使用了常数级额外空间。

六、个人总结与思考

  • 问题拆解的重要性
    原公式复杂,但通过公式拆解,问题转化为一个维护动态最大值的过程,极大简化了思路。

  • 动态规划思想
    本问题虽不严格是动态规划,但引入了“状态维护”的概念,这与动态规划的思想如出一辙。通过维护状态变量 max_i_plus_values,将原问题的复杂度从 O(n2)O(n^2) 降至 O(n)O(n)。

  • 扩展与应用
    类似问题常出现在路径规划、股票买卖问题中,核心在于如何维护并更新动态变量。例如:

    • 股票买卖中的最大收益问题。
    • 寻找数组中满足某种约束的最大子段和。

通过学习该问题,不仅巩固了数学建模能力,也强化了动态规划与贪心算法的实际应用能力。


希望以上笔记能够帮助您更好地理解和解决此类问题!