题目:
给定正整数数组 A,A[i] 表示第 i 个观光景点的评分,并且两个景点 i 和 j 之间的距离为 j - i。
一对景点(i < j)组成的观光组合的得分为(A[i] + A[j] + i - j):景点的评分之和减去它们两者之间的距离。
返回一对观光景点能取得的最高分。
示例:
输入:[8,1,5,2,6]
输出:11
解释:i = 0, j = 2, A[i] + A[j] + i - j = 8 + 5 + 0 - 2 = 11
提示:
2 <= A.length <= 50000
1 <= A[i] <= 1000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-sightseeing-pair
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路:
遍历出所有的组合,获得得分最大的组合,且因为是两个景点,所以不会有 1-1, 2-2的组合。同时1-2,2-1的组合是同一个组合,所以外层循环从0开始,循环到A.length-1即可,i-j即为距离,所以内层循环以距离为标准,从1开始,截止到A.length-i.记录最大的得分,最后返回。
我的代码:
class Solution {
public int maxScoreSightseeingPair(int[] A) {
// 默认得分为0
int maxScore = 0;
for(int i = 0; i < A.length-1; i++) {
// j代表i与下一个景点的距离
for(int j = 1; j < A.length-i; j++) {
// 两个景点的得分减去距离
maxScore = Math.max(maxScore, A[i] + A[i+j] - j);
}
}
return maxScore;
}
}
运行结果:
超时… 接着我去查看了题解:
官方题解:
方法一:枚举 思路和算法
我们考虑从前往后枚举 j j j 来统计答案,对于每个观光景点 j j j 而言,我们需要遍历 [ 0 , j − 1 ] [0,j-1] [0,j−1]的观光景点 i i i 来计算组成观光组合 ( i , j ) (i,j) (i,j)得分的最大值 c n t j {cnt}_j cntj 来作为第 j j j个观光景点的值,那么最后的答案无疑就是所有观光景点值的最大值,即 m a x j = 0.. n − 1 { c n t j } max_{j=0..n-1}\{cnt_j\} maxj=0..n−1{cntj}。但是枚举 j j j 需要 O ( n ) O(n) O(n) 的时间复杂度,遍历 [ 0 , j − 1 ] [0,j-1] [0,j−1]的观光景点 i i i 也需要 O ( n ) O(n) O(n) 的时间复杂度,因此该方法总复杂度为 O ( n 2 ) O(n^2) O(n2) ,不能通过所有测试用例,我们需要进一步优化时间复杂度。
我们回过头来看得分公式,我们可以将其拆分成 A [ i ] + i A[i]+i A[i]+i 和 A [ j ] − j A[j]-j A[j]−j 两部分,这样对于统计景点 j j j答案的时候,由于 A [ j ] − j A[j]-j A[j]−j 是固定不变的,因此最大化 A [ i ] + i + A [ j ] − j A[i]+i+A[j]-j A[i]+i+A[j]−j的值其实就等价于求 [ 0 , j − 1 ] [0,j-1] [0,j−1]中 A [ i ] + i A[i]+i A[i]+i 的最大值 m x mx mx,景点 j j j 的答案即为 m x + A [ j ] − j mx+A[j]-j mx+A[j]−j 。而 m x mx mx 的值我们只要从前往后枚举 j j j 的时候同时维护即可,这样每次枚举景点 j j j的时候,寻找使得得分最大的 i i i 就能从 O ( n ) O(n) O(n) 降至 O ( 1 ) O(1) O(1) 的时间复杂度,总时间复杂度就能从 O ( n 2 ) O(n^2) O(n2) 降至 O ( n ) O(n) O(n)。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/best-sightseeing-pair/solution/zui-jia-guan-guang-zu-he-by-leetcode-solution/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
根据题解,我们发现解题的关键在于 A [ i ] + A [ j ] + i − j A[i] + A[j] + i - j A[i]+A[j]+i−j这个算式上面,其可以分为 A [ i ] + i A[i] + i A[i]+i和 A [ j ] − j A[j] - j A[j]−j两部分,我们只需在遍历过程中记录遍历过的一部分的最大值(即当得分最大时,这两部分肯定为非相同景点下对应的最大值),再拿这个最大值与当前遍历节点得分相加即可得到相对较大的得分,再与已记录的最大得分比较,取最大的。遍历结束时即可得到最大得分,根据这个思路,可以得到如下接答。
官方思路下的个人解题:
class Solution {
public int maxScoreSightseeingPair(int[] A) {
int left = A[0] + 0;
int maxScope = 0;
for(int i = 1; i < A.length; i++) {
// 算出当前节点与之前节点最大值的和得出相对最大值
maxScope = Math.max(maxScope, left + A[i] - i);
// 记录遍历过的最大得分
left = Math.max(left, A[i] + i);
}
return maxScope;
}
}
运行结果:
官方题解下的其它联想:
因为在题解中我们可以看到我们实际求的是
A
[
i
]
+
i
A[i] + i
A[i]+i和
A
[
j
]
−
j
A[j] - j
A[j]−j两部分的相应最大值,我们可以记录
A
[
i
]
+
i
A[i] + i
A[i]+i的最大值,自然也可以记录
A
[
j
]
−
j
A[j] - j
A[j]−j的最大值,然后遍历
A
[
i
]
+
i
A[i] + i
A[i]+i。但是这样我们需要从后往前遍历,记录
A
[
j
]
−
j
A[j] - j
A[j]−j,然后遍历最大值。不能从前遍历。因为如果从前遍历就变为
A
[
i
]
−
i
+
A
[
j
]
+
j
A[i] - i + A[j] + j
A[i]−i+A[j]+j,计算总得分的方式就变为得分之和+加上两者之间的距离了,违背了题意。
从后往前遍历下的解答方式:
class Solution {
public int maxScoreSightseeingPair(int[] A) {
// 最右边的值
int right = A[A.length-1] - A.length + 1;
int maxScope = 0;
// 从右边第二个开始往前遍历
for(int i = A.length-2; i >= 0 ; i--) {
maxScope = Math.max(maxScope, right + A[i] + i);
right = Math.max(right, A[i] - i);
}
return maxScope;
}
}