题解:学而思编程 国庆旅行

【题目来源】

学而思编程:国庆旅行

【题目描述】

小猴国庆节来北京玩,北京有 \(n\) 个景点,第 \(i\) 个景点的评分用 \(a_i\) 表示,两个景点 \(i\)\(j\) 之间的距离为 \(j-i(j>i)\)

小猴一天只能游玩两个景点,我们认为总评分是两个景点的评分之和减去两个景点之间的距离,即为 \(a[i]+a[j]-(j-i)\) 其中 \(i<j\)

那么小猴选择哪两个景点才会总评分最大呢?

【输入】

第一行包含整数 \(n\)

第二行分别输入 \(n\) 个景点的评分 \(a_1,a_2,\dots, a_n\)

【输出】

一行,一个整数,表示输出最大评分。

【输入样例】

5
11 6 5 18 12

【输出样例】

29

【核心思想】

  1. 问题分析:给定 \(n\) 个景点的评分 \(a_i\),需要选择两个景点 \(i < j\),使得总评分 \(a_i + a_j - (j - i)\) 最大。将表达式变形:\(a_i + a_j - j + i = (a_i + i) + (a_j - j)\)。关键观察是:对于固定的 \(j\),需要找到 \(i < j\) 使得 \(a_i + i\) 最大。因此可以预处理前缀最大值或后缀最大值来优化。

  2. 算法选择

    • 后缀最大值预处理:定义 \(s[i] = \max_{k \geq i} (a_k - k)\),则从右往左递推计算
    • 线性扫描求答案:枚举左端点 \(i\),利用后缀最大值数组 \(O(1)\) 获取最优右端点
  3. 关键步骤

    • 读取输入\(n\) 和数组 \(a[1..n]\)
    • 后缀最大值预处理
      • \(s[n+1] = -\infty\)(哨兵值)
      • \(i\)\(n\)\(1\)\(s[i] = \max(s[i+1], a[i] - i)\)
      • \(s[i]\) 表示从位置 \(i\)\(n\) 中,\(a_k - k\) 的最大值
    • 计算最大评分
      • 初始化 \(\text{ans} = -\infty\)
      • \(i\)\(1\)\(n-1\)\(\text{ans} = \max(\text{ans}, a[i] + i + s[i+1])\)
      • 其中 \(a[i] + i\) 是固定左端点 \(i\) 的贡献,\(s[i+1]\) 是右端点 \(j > i\) 的最优贡献
    • 输出答案\(\text{ans}\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),后缀预处理 \(O(n)\),扫描求答案 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储原数组和后缀最大值数组
  5. 后缀最大值预处理的核心思想

    • 表达式拆分:将原式 \(a_i + a_j - (j - i)\) 拆分为 \((a_i + i) + (a_j - j)\),使左右两端点独立,分别只与自身下标相关
    • 后缀数组优化\(s[i]\) 预处理了从位置 \(i\) 开始的所有 \(a_k - k\) 的最大值,使得枚举左端点时,右端点的最优值可以 \(O(1)\) 获取
    • 递推构建\(s[i] = \max(s[i+1], a[i] - i)\) 从右往左递推,每个位置只需比较当前值与已计算的后缀值,体现了动态规划的最优子结构
    • 哨兵处理\(s[n+1] = -\infty\) 确保边界安全,使得 \(i = n\) 时的 \(s[i+1]\) 有定义(虽然实际不会用到)
    • 适用于双变量优化问题中,通过代数变形将耦合项分离,再利用前缀/后缀最值实现线性时间求解

【解题思路】

【算法标签】

前缀和

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int a[100010], s[100010];  // a: 原始数组, s: 后缀最大值数组
int main()
{
    int n;
    cin >> n;  // 输入数组长度

    for (int i = 1; i <= n; i++)  // 输入数组元素
    {
        cin >> a[i];
    }

    // 初始化边界
    s[n + 1] = -1e9;  // 哨兵值

    // 计算后缀最大值 s[i] = max(a[i]-i, a[i+1]-(i+1), ..., a[n]-n)
    for (int i = n; i >= 1; i--)
    {
        s[i] = max(s[i + 1], a[i] - i);
    }

    int maxn = -1e9;  // 初始化答案

    // 计算最大值:a[i] + i + s[i+1]
    for (int i = 1; i <= n; i++)
    {
        maxn = max(maxn, a[i] + i + s[i + 1]);
    }

    cout << maxn;  // 输出结果
    return 0;
}

【运行结果】

5
11 6 5 18 12
29
posted @ 2026-07-02 22:45  团爸讲算法  阅读(5)  评论(0)    收藏  举报