P11963 [GESP202503 六级] 环线
题目描述
小 A 喜欢坐地铁。地铁环线有 n n n 个车站,依次以 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n 标号。车站 i ( 1 ≤ i < n ) i\ (1\leq i<n) i (1≤i<n) 的下一个车站是车站 i + 1 i+1 i+1。特殊地,车站 n n n 的下一个车站是车站 1 1 1。
小 A 会从某个车站出发,乘坐地铁环线到某个车站结束行程,这意味着小 A 至少会经过一个车站。小 A 不会经过一个车站多次。当小 A 乘坐地铁环线经过车站 i i i 时,小 A 会获得 a i a_i ai 点快乐值。请你安排小 A 的行程,选择出发车站与结束车站,使得获得的快乐值总和最大。
输入格式
第一行,一个正整数 n n n,表示车站的数量。
第二行, n n n 个整数 a i a_i ai,分别表示经过每个车站时获得的快乐值。
输出格式
一行,一个整数,表示小 A 能获得的最大快乐值。
输入输出样例 #1
输入 #1
4
-1 2 3 0
输出 #1
5
输入输出样例 #2
输入 #2
5
-3 4 -5 1 3
输出 #2
5
说明/提示
对于 20 % 20\% 20% 的测试点,保证 1 ≤ n ≤ 200 1\leq n\leq 200 1≤n≤200。
对于 40 % 40\% 40% 的测试点,保证 1 ≤ n ≤ 2000 1\leq n\leq 2000 1≤n≤2000。
对于所有测试点,保证 1 ≤ n ≤ 2 × 1 0 5 1\leq n\leq 2\times 10^5 1≤n≤2×105, − 1 0 9 ≤ a i ≤ 1 0 9 -10^9\leq a_i\leq 10^9 −109≤ai≤109。
解析
一共分三种情况
1.所有站点快乐值都是负数,那么只能去最大的那个站点;
2.不跨过1点和n点,那么可以取1点到n点之间的最大连续字段和(贪心);
3.如果跨过1点和n点,可以将路程分为两段,一段从i+1点到n点,一段从1点到i点,可以预处理这两段的快乐值最大值,然后枚举i,取最大值。
详见代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[200005];//站点快乐值
long long q[200005];//q[i]表示从1出发不超过i,可以获得的最大快乐值
long long h[200005];//h[i]表示从n出发不超过i,可以获得的最大快乐值
long long ans = -2e9;
int main() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
ans = max(int(ans), a[i]); //取最大站点
}
if (ans < 0) { //如果所有车站的快乐值都是负数,则只去那个快乐值最大的
cout << ans;
return 0;
}
long long sum = 0;
for(int i = 1; i <= n; i++) { //不跨过1点和n点的情况,取最大连续子段和
sum += a[i];
if (sum < 0) sum = 0;
ans = max(ans, sum);
}
sum = 0;
for(int i = 1; i <= n; i++) { //计算q[i]
sum += a[i];
q[i] = max(q[i - 1], sum);
}
sum = 0;
for(int i = n; i >= 1; i--) { //计算h[i]
sum += a[i];
h[i] = max(h[i + 1], sum);
}
//枚举分界点
for(int i = 1; i < n; i++) {
//取从i+1点经过n点和1点再到i点这段距离可以获得最大快乐值的最大值
ans = max(ans, q[i] + h[i + 1]);
}
cout << ans;
return 0;
}
官方给的代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 4e5 + 5;
int n;
long long a[N], pre[N];
int q[N], ql, qr;
long long ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
a[n + i] = a[i];
}
for (int i = 1; i <= 2 * n; i++)
pre[i] = pre[i - 1] + a[i];
ql = qr = 1;
ans = -1e18;
for (int i = 1; i <= 2 * n; i++) {
while (ql <= qr && q[ql] < i - n)
ql++;
ans = max(ans, pre[i] - pre[q[ql]]);
while (ql <= qr && pre[i] < pre[q[qr]])
qr--;
q[++qr] = i;
}
printf("%lld\n", ans);
return 0;
}