问题描述
小R正在研究一组观光景点,每个景点都有一个评分,保存在数组 values 中,其中 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] + values[j],以及它们下标差的相反数i - j。 - 由于
i < j,i - j总是负数,所以我们实际上是在最小化j - i。
- 得分由两部分组成:两个景点的评分之和
-
动态规划状态定义:
- 定义
dp[i]为考虑前i个景点,以第i个景点结尾时的最大得分。
- 定义
-
状态转移方程:
- 对于每个景点
i,我们有两种选择:不选择它,或者选择它。 - 如果不选择第
i个景点,那么最大得分就是dp[i-1]。 - 如果选择第
i个景点,我们需要找到之前的最佳景点k(k < i),使得dp[k] + values[i] + k - i最大。
- 对于每个景点
-
优化:
- 由于
k - i是负数,我们实际上希望k尽可能小,这样k - i的绝对值就尽可能大。 - 因此,我们只需要维护一个变量
max_score来记录到目前为止的最大得分,每次更新dp[i]时,我们比较max_score和当前的得分,取较大值。
- 由于
-
实现:
- 初始化
dp[0]为values[0],因为只有一个景点时,最大得分就是其自身的评分。 - 遍历每个景点
i,更新dp[i]和max_score。 dp[i]的值是values[i] + i和dp[i-1]中的较大值,因为我们需要最大化当前景点的得分。max_score的值是dp[i-1] + values[i] - i和当前max_score中的较大值,因为我们需要最大化两个景点的组合得分。
- 初始化
-
返回结果:
- 遍历结束后,
max_score就是全局最大得分。
- 遍历结束后,
这种方法的优点是时间复杂度较低,只需要 O(n) 的时间,空间复杂度也较低,只需要 O(n) 的空间。它利用了问题的特定性质,通过维护一个当前最大得分的变量来避免复杂的状态转移,从而简化了问题。
代码实现
def solution(values: list) -> int:
n = len(values)
if n < 2: # 景区数小于2,则得分为0
return 0
# 初始化dp数组,dp[i]表示以第i个景点结尾的最大得分
dp = [0] * n
dp[0] = values[0]
max_score = float('-inf') # 初始化最大得分为负无穷
for i in range(1, n):
# 计算以第i个景点结尾的最大得分
dp[i] = max(dp[i-1], values[i] + i)
# 更新全局最大得分
max_score = max(max_score, dp[i-1] + values[i] - i)
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)
上述代码的时间复杂度和空间复杂度均为O(n)。