CCF-GESP计算机学会等级考试2025年3月六级C++T2 环线

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 (1i<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 1n200

对于 40 % 40\% 40% 的测试点,保证 1 ≤ n ≤ 2000 1\leq n\leq 2000 1n2000

对于所有测试点,保证 1 ≤ n ≤ 2 × 1 0 5 1\leq n\leq 2\times 10^5 1n2×105 − 1 0 9 ≤ a i ≤ 1 0 9 -10^9\leq a_i\leq 10^9 109ai109

解析

一共分三种情况
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长春高老师信奥工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值