观光景点组合得分问题
一、问题描述
小红正在研究一组观光景点组合问题,每个景点都有一个评分,保存在数组 values 中,其中 values[i] 表示第 ii 个景点的评分。同时,两个景点之间的得分计算公式为: 得分=values[i]+values[j]+i−j\text{得分} = \text{values}[i] + \text{values}[j] + i - j 其中 i<ji < j。
现在小红需要知道,哪种组合能使得得分最大。
输入与输出
- 输入:一个整数数组
values,表示各个景点的评分。 - 输出:一个整数,表示组合得分的最大值。
示例
- 输入:
values = [8, 3, 5, 5, 6]
输出:11
解释:最佳组合是景点(0, 4),得分为 8+6+0−4=118 + 6 + 0 - 4 = 11。 - 输入:
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,利用当前维护的最大值,计算组合得分,并更新最大得分。
三、解题思路
-
初始化变量
- 设
max_i_plus_values保存当前已遍历的最大值 values[i]+i\text{values}[i] + i。 - 设
max_score保存当前最大得分。
- 设
-
遍历数组
- 从第二个元素开始,逐步计算以 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。
-
返回结果
- 遍历结束后,返回
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
五、测试与分析
-
正确性测试
通过给定的示例,验证程序输出是否正确:print(solution([8, 3, 5, 5, 6])) # 输出: 11 print(solution([10, 4, 8, 7])) # 输出: 16 print(solution([1, 2, 3, 4, 5, 6])) # 输出: 8 -
边界条件
- 数组长度为 1:返回 0,因为没有可选组合。
- 数组全为负值:程序能够正常运行并计算负值组合。
-
时间复杂度
- 遍历数组一次,时间复杂度为 O(n)O(n)。
- 空间复杂度为 O(1)O(1),仅使用了常数级额外空间。
六、个人总结与思考
-
问题拆解的重要性
原公式复杂,但通过公式拆解,问题转化为一个维护动态最大值的过程,极大简化了思路。 -
动态规划思想
本问题虽不严格是动态规划,但引入了“状态维护”的概念,这与动态规划的思想如出一辙。通过维护状态变量max_i_plus_values,将原问题的复杂度从 O(n2)O(n^2) 降至 O(n)O(n)。 -
扩展与应用
类似问题常出现在路径规划、股票买卖问题中,核心在于如何维护并更新动态变量。例如:- 股票买卖中的最大收益问题。
- 寻找数组中满足某种约束的最大子段和。
通过学习该问题,不仅巩固了数学建模能力,也强化了动态规划与贪心算法的实际应用能力。
希望以上笔记能够帮助您更好地理解和解决此类问题!