观光景点组合得分问题
问题描述
小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
Code:
using namespace std;
int solution(vector<int> values) {
if(values.size() == 1) return 0;
int maxv = 0 , res = 0;
for(int i = 0 ; i < values.size() ; i ++)
{
res = max(res , values[i] - i + maxv);
maxv = max(maxv , values[i] + i);
}
return res; // Placeholder return
}
int main() {
cout << (solution({8, 3, 5, 5, 6}) == 11) << endl;
cout << (solution({10, 4, 8, 7}) == 16) << endl;
cout << (solution({1, 2, 3, 4, 5}) == 8) << endl;
return 0;
}
题目分析与理解
在这个问题中,我们给定了一个整数数组 values,目标是通过某种特定的方式计算并找到最大值。通过分析代码,发现问题的本质在于结合数组中的元素值及其索引,通过特定的公式来计算出一个结果。
在这个过程中,最关键的步骤是理解公式 res = max(res, values[i] - i + maxv) 和 maxv = max(maxv, values[i] + i) 的含义及其计算逻辑。下面我们将详细分析题目的要求,逐步解析代码的实现,并总结出解题的思路和优化方法。
问题的关键公式
题目中的核心公式是:
res = max(res, values[i] - i + maxv)
和
maxv = max(maxv, values[i] + i)
我们需要通过这两个公式来找到最大值。这是基于以下几方面的考虑:
-
数组元素与其索引的关系:
- 对于数组中的每个元素
values[i],我们要考虑它与其索引i结合后的某种表现形式,计算出一个最优值。 - 通过公式
values[i] - i + maxv,我们希望得到当前元素values[i]与它的索引i的差值,并加上之前计算出的最大参考值maxv。
- 对于数组中的每个元素
-
maxv 的更新:
maxv = max(maxv, values[i] + i),该公式的意思是我们要保持追踪数组中每个元素与其索引i相加后的最大值。maxv记录的是迄今为止,我们计算出的最大参考值。- 通过这个更新过程,我们能够保持一个有效的历史记录,在后续的计算中使用这些最大值来与当前的值做对比,从而得到最优的结果。
解题思路分析
本题的思路实际上是通过动态维护一个历史最大值 maxv 来避免重复计算,并且通过每次计算 values[i] - i + maxv 来得到一个新的可能最大值。我们将这个过程逐步分解如下:
-
初始化:
maxv初始化为 0,它将用于记录数组中每个位置之前(包括当前位置)的最大值values[i] + i。res初始化为 0,用于记录当前计算过程中的最大值。最终的输出结果就是res的值。
-
遍历数组:
-
遍历数组
values中的每个元素values[i],对每个元素,使用公式res = max(res, values[i] - i + maxv)进行更新。- 其中,
values[i] - i是当前元素和索引的差,maxv是历史最大值,表示此前元素索引加值的最大值。 - 每次更新
res时,计算出当前值与历史最大值结合的最优解。
- 其中,
-
然后,更新
maxv为max(maxv, values[i] + i),即比较当前值和索引相加的结果与之前的maxv,保持maxv为最大的参考值。
-
-
结束遍历,输出结果:
- 当遍历完所有元素后,
res存储了我们要求的最大值。
- 当遍历完所有元素后,
代码实现分析
#include <bits/stdc++.h>
using namespace std;
int solution(vector<int> values) {
if(values.size() == 1) return 0;
int maxv = 0, res = 0;
for(int i = 0 ; i < values.size() ; i++) {
res = max(res, values[i] - i + maxv);
maxv = max(maxv, values[i] + i);
}
return res; // Placeholder return
}
int main() {
cout << (solution({8, 3, 5, 5, 6}) == 11) << endl;
cout << (solution({10, 4, 8, 7}) == 16) << endl;
cout << (solution({1, 2, 3, 4, 5}) == 8) << endl;
return 0;
}
1. 初始化部分
int maxv = 0, res = 0;
这两行代码分别初始化了 maxv 和 res。maxv 用来追踪到当前位置为止最大的 values[i] + i,res 用来存储每次计算得到的最大结果。
2. 遍历部分
for(int i = 0; i < values.size(); i++) {
res = max(res, values[i] - i + maxv);
maxv = max(maxv, values[i] + i);
}
这个循环遍历了数组中的所有元素。每次循环都用 values[i] - i + maxv 计算当前可能的最大值,并更新 maxv。通过不断更新 res 和 maxv,我们确保能够找到最终的最大结果。
3. 输出结果
return res;
返回最终的最大值 res,这是我们经过遍历和计算得到的结果。
示例分析
示例 1
输入:{8, 3, 5, 5, 6}
-
初始时,
maxv = 0和res = 0。 -
遍历每个元素:
i = 0,res = max(0, 8 - 0 + 0) = 8,更新maxv = max(0, 8 + 0) = 8。i = 1,res = max(8, 3 - 1 + 8) = 10,maxv = max(8, 3 + 1) = 8。i = 2,res = max(10, 5 - 2 + 8) = 11,maxv = max(8, 5 + 2) = 7。i = 3,res = max(11, 5 - 3 + 8) = 11,maxv = max(7, 5 + 3) = 8。i = 4,res = max(11, 6 - 4 + 8) = 11,maxv = max(8, 6 + 4) = 10。
-
最终结果为
11。
示例 2
输入:{10, 4, 8, 7}
-
初始时,
maxv = 0和res = 0。 -
遍历每个元素:
i = 0,res = max(0, 10 - 0 + 0) = 10,更新maxv = max(0, 10 + 0) = 10。i = 1,res = max(10, 4 - 1 + 10) = 13,maxv = max(10, 4 + 1) = 10。i = 2,res = max(13, 8 - 2 + 10) = 16,maxv = max(10, 8 + 2) = 10。i = 3,res = max(16, 7 - 3 + 10) = 16,maxv = max(10, 7 + 3) = 10。
-
最终结果为
16。
时间与空间复杂度分析
- 时间复杂度:
本算法的时间复杂度为O(n),其中n是数组values的长度。我们只遍历了一次数组,计算每个元素的值,因此时间复杂度为线性。 - 空间复杂度:
空间复杂度为O(1),因为我们只用了常数个额外变量maxv和res来存储计算结果。
总结
- 本题考察了动态更新和参考最大值的技巧,通过不断更新
maxv和res,我们能够在一次遍历中获得最终的最大结果。 - 通过有效地使用前缀最大值技巧,我们成功减少了重复计算,提高了算法的效率。
- 时间和空间复杂度均为
O(n)和O(1),算法效率较高,能够适应题目要求的输入规模。