题目解析
题目
小R正在研究一组观光景点,每个景点都有一个评分,保存在数组 value 中,其中 values[i] 表示第 i 个观光景点的评分。同时,景点之间的距离由它们的下标差 j-i表示一对景点 (i<j) 的观光组合得分为 values[i] + values[j] + i - j ,也就是两者评分之和减去它们之间的距离。小R想知道,在哪种情况下能够获得观光景点组合的最高得分。
测试用例
样例1:
输入:
values = [8, 3, 5, 5, 6]
输出:11
样例2:
输入:
values = [10, 4, 8, 7]
输出:16
样例3:
输入:
values = [1, 2, 3, 4, 5]
输出:8
具体解法
剖析
题目的关键点在于values[i] + values[j] + i - j的求解
我们把公式拆解为两部分:
-
values[i] + i是关于第一个景点i的信息; -
values[j] - j是关于第二个景点j的信息。 这样,我们的目标就是在i < j的情况下,找到最大的values[i] + i + values[j] - j。
解法思路
我们可以遍历数组 values,对于每一个 j 值,我们去寻找在 j 之前的所有 i 的最大值 values[i] + i。
记录遍历过程中最大的 values[i] + i 的值,用它和当前的 values[j] - j 相加,得到最大可能的得分。
更新遍历中的最大得分,最终得到结果。
代码实现
定义max_score用于记录最大得分
max_score = 0
初始化 values[i] + i 最大值为第一个景点的值加上它的索引
max_i_value = values[0] + 0
遍历元素,更新最大值 从第二个元素开始遍历
for j in range(1, len(values)):
max_score = max(max_score, max_i_value + values[j] - j)
max_i_value = max(max_i_value, values[j] + j)
循环结束返回最大得分
return max_score
注意事项
在 for循环中,要先更新 max_score 而不是 max_i_value
因为我们希望在计算得分时使用的 max_i_value 是从 j 的前一个位置及之前的位置中得到的最大值,而不是当前 j 位置的值。即要满足题中i<j的条件
知识总结
该题目综合运用了动态规划的状态更新、前缀和的优化思路、贪心算法的当前最优选择以及动态最大值的维护等知识。
我们引入了 max_i_value,表示 values[i] + i 在 j 之前位置的最大值,这样对于每个 j,可以在常数时间内得出 i < j 的前缀最大值,而无需重新遍历之前的元素。
在每次遍历时,我们更新 max_score,确保每一步都保持当前的最优解。
这是贪心思想的体现,在动态增长的过程中始终选择当前最优状态,从而在遍历结束时得到全局最优解
代码
def solution(values: list) -> int:
max_score = 0
max_i_value = values[0] + 0 # 初始化最大值为第一个景点的值加上它的索引
# 从第二个元素开始遍历
for j in range(1, len(values)):
# 计算得分
max_score = max(max_score, max_i_value + values[j] - j)
# 更新 max_i_value 为前 j 个景点的最大值
max_i_value = max(max_i_value, values[j] + j)
return max_score
if __name__ == '__main__':
print(solution(values=[8, 3, 5, 5, 6]) == 11)
print(solution(values=[10, 4, 8, 7]) == 16)
print(solution(values=[1, 2, 3, 4, 5]) == 8)