Leetcode 79 最佳观光组合

1 题目

1014. 最佳观光组合

给你一个正整数数组 values,其中 values[i] 表示第 i 个观光景点的评分,并且两个景点 ij 之间的 距离j - i

一对景点(i < j)组成的观光组合的得分为 values[i] + values[j] + i - j ,也就是景点的评分之和减去它们两者之间的距离。

返回一对观光景点能取得的最高分。

示例 1:

复制代码
输入:values = [8,1,5,2,6]
输出:11
解释:i = 0, j = 2, values[i] + values[j] + i - j = 8 + 5 + 0 - 2 = 11

示例 2:

复制代码
输入:values = [1,2]
输出:2

2 代码实现

cpp 复制代码
class Solution {
public:
    int maxScoreSightseeingPair(vector<int>& values) {
        int best = values[0]; 
        int ans = 0;

        for (int j = 1 ; j < values.size() ; j++){
            ans = max (ans, best + values[j] - j  );

            best = max(best , values[j] + j );
        }

        return ans;
    }
};

题解

思路一句话版

把公式

values[i] + values[j] + i - j

拆成

(values[i] + i) + (values[j] - j)

遍历 j 的时候,只需要维护 j 左边最大的 values[i] + i

C++ 代码(推荐写法)

cpp 复制代码
#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    int maxScoreSightseeingPair(vector<int>& values) {
        int best = values[0];  // 当前最大的 values[i] + i
        int ans = 0;

        for (int j = 1; j < values.size(); j++) {
            // 用当前 best 和 j 计算得分
            ans = max(ans, best + values[j] - j);

            // 更新 best,供后面的 j 使用
            best = max(best, values[j] + j);
        }

        return ans;
    }
};

为什么是 O(n)?

  • 每个元素只遍历一次

  • 只用了两个变量

  • 时间复杂度:O(n)

  • 空间复杂度:O(1)


再帮你对比一下暴力解法(为什么不行)

暴力写法(会超时)
cpp 复制代码
for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
        ans = max(ans, values[i] + values[j] + i - j);
    }
}
  • 时间复杂度:O(n²)

  • n 最大到 5 万,直接炸 💥


记忆口诀(刷题用)

i 在左,j 在右
i 的最大值提前存
j 来了直接算

详解这段代码中循环里计算得分和更新best的逻辑

首先,我们先明确这道题的核心公式:对于观光配对(i, j)(要求i < j),得分是values[i] + values[j] + i - j。我们可以把这个公式拆分成两部分:

  • 第一部分:values[i] + i(只和i有关)
  • 第二部分:values[j] - j(只和j有关)

所以得分就等价于**(values[i] + i) + (values[j] - j)** (i < j)。

代码逻辑逐行解释

cpp 复制代码
class Solution {
public:
    int maxScoreSightseeingPair(vector<int>& values) {
        int best = values[0];  // 初始化为 values[0] + 0(因为i=0时,values[i]+i就是values[0]+0)
        int ans = 0;

        for (int j = 1; j < values.size(); j++) {
            // 第一步:用当前找到的最优i(对应best=values[i]+i)和当前j计算得分
            // 得分 = (values[i]+i) + (values[j]-j) = best + values[j] - j
            ans = max(ans, best + values[j] - j);

            // 第二步:更新best,把当前j当作新的i候选(因为后面的j' > j,此时j就是i)
            // 新的候选值是 values[j] + j,和原来的best比,保留更大的那个
            best = max(best, values[j] + j);
        }

        return ans;
    }
};

举个例子帮你理解

假设values = [8,1,5,2,6],我们一步步走一遍:

  1. 初始化:best = 8 + 0 = 8ans = 0
  2. j=1 (valuesj=1):
    • 得分:8 + 1 - 1 = 8ans = 8
    • 更新 best:max(8, 1+1=2)best=8
  3. j=2 (valuesj=5):
    • 得分:8 + 5 - 2 = 11ans = 11
    • 更新 best:max(8, 5+2=7)best=8
  4. j=3 (valuesj=2):
    • 得分:8 + 2 - 3 = 7ans 还是 11
    • 更新 best:max(8, 2+3=5)best=8
  5. j=4 (valuesj=6):
    • 得分:8 + 6 - 4 = 10ans 还是 11
    • 更新 best:max(8, 6+4=10)best=10
  6. 最终返回ans=11(对应 i=0,j=2 的配对,得分 8+5+0-2=11,和计算结果一致)

总结

  1. 公式拆分是核心 :将原得分公式values[i]+values[j]+i-j拆分为(values[i]+i) + (values[j]-j),把双变量问题转化为单变量的遍历问题。
  2. 贪心维护最优值 :遍历j时,用best维护之前所有i < jvalues[i]+i的最大值,这样每一步只需要 O (1) 计算,整体时间复杂度是 O (n)。
  3. 遍历顺序 :因为要求i < j,所以从j=1开始遍历,先计算当前j的得分,再更新best(避免 j 自己和自己配对)。
相关推荐
其实防守也摸鱼12 分钟前
前端应用的离线暂停更新策略:构建稳定可靠的渐进式部署方案
服务器·前端·数据库·学习·ai·命令行·linux系统
MartinYeung532 分钟前
[论文学习]Mamba:具有选择性状态空间的线性时间序列建模
学习
j7~34 分钟前
【数据结构初阶】队列的实现(链式队列 + 循环队列)--详解
c语言·开发语言·数据结构·学习·队列·queue·c\c++
道影子1 小时前
《道德经》031兵者不祥,胜以丧礼处之
人工智能·深度学习·算法
花生了什么事o1 小时前
分布式 ID 生成方案:从数据库自增到雪花算法
数据库·分布式·算法
king_linlin1 小时前
算法基础——算法复杂度
c语言·开发语言·数据结构·算法
沐苏瑶1 小时前
计算机网络核心笔记:打通 TCP/UDP 与 HTTP/HTTPS 底层逻辑(重点上)
笔记·计算机网络·http·https
天天爱吃肉82182 小时前
AI Agent时代你的经验值多少钱?
人工智能·python·功能测试·学习·汽车
king_linlin2 小时前
数据结构——顺序表(附图文讲解|超详细)
c语言·数据结构·算法
GAOJ_K2 小时前
老旧产线弧形导轨换新:同规格替换避坑选型思路
人工智能·算法·机器学习·制造·导轨偏磨