题解:学而思编程 国庆旅行
【题目来源】
学而思编程:国庆旅行
【题目描述】
小猴国庆节来北京玩,北京有 \(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
【核心思想】
-
问题分析:给定 \(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\) 最大。因此可以预处理前缀最大值或后缀最大值来优化。
-
算法选择:
- 后缀最大值预处理:定义 \(s[i] = \max_{k \geq i} (a_k - k)\),则从右往左递推计算
- 线性扫描求答案:枚举左端点 \(i\),利用后缀最大值数组 \(O(1)\) 获取最优右端点
-
关键步骤:
- 读取输入:\(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}\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),后缀预处理 \(O(n)\),扫描求答案 \(O(n)\)
- 空间复杂度:\(O(n)\),存储原数组和后缀最大值数组
-
后缀最大值预处理的核心思想:
- 表达式拆分:将原式 \(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
浙公网安备 33010602011771号