JoyOI 1305\CH1201最大子序和

16 篇文章 0 订阅
题目描述
输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6

输入格式
第一行两个数n,m
第二行有n个数,要求在n个数找到最大子序和

输出格式
一个数,数出他们的最大子序和

输入样例 #1	
6 4
1 -3 5 1 -2 3

输出样例 #1
7

前缀和+单调队列

问题区间 [ i , j ] [i,j] [i,j] 的中子序和可以根据前缀和得出: s u m = a r r [ j ] − a r r [ i − 1 ] sum=arr[j]-arr[i-1] sum=arr[j]arr[i1]

所以将问题转化为寻找两个位置 i , j i,j i,j 使得区间 [ i , j ] [i,j] [i,j] 内子序和最大,并且 j − i ≤ M j-i \le M jiM

我们通过枚举右端点 j ​ j​ j 得到答案,当 j ​ j​ j 固定时,问题又转化为寻找使 a r r [ i ] ​ arr[i]​ arr[i] 最小的 i ​ i​ i ,并且 i ∈ [ j − M , j − 1 ] ​ i \in [j-M,j-1]​ i[jM,j1]


可以用单调队列 q q q 来保存这些 位置 ,思想是在队列中及时排除一定不是最优解的元素

我们定义队首为 l l l ,队尾为 r r r ,对右端点 i i i 操作(先进先出 l l l 先进, r r r 先出),队首元素中保存着最优位置

1、判断队首元素 p [ l ] p[l] p[l] i i i 的距离是否大于 M,大于将队首元素出队

2、更新答案 a n s = m a x ( a n s , a r r [ i ] − a r r [ q [ l ] ] ) ans = max(ans, arr[i] - arr[q[l]]) ans=max(ans,arr[i]arr[q[l]])

3、判断当前元素与队尾元素的前缀和大小,此时 i i i 是一定比 q [ r ] q[r] q[r] 大的,因为后进的嘛,如果 a r r [ q [ r ] ] > = a r r [ i ] arr[q[r]]>=arr[i] arr[q[r]]>=arr[i] 说明 a r r [ q [ r ] ] arr[q[r]] arr[q[r]] 已经不可能是最优解了,轮不到 a r r [ q [ r ] ] arr[q[r]] arr[q[r]] 做贡献了,将其出队

在本题的单调队列中元素是递增的 q [ l ] &lt; q [ r ] q[l]&lt;q[r] q[l]<q[r] ,当位置 q [ l ] q[l] q[l] 因为超过M而不合法时,下一个最小的前缀和出来做贡献,以此类推

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
static const auto io_sync_off = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    return nullptr;
}();

const int maxn = 300005;
int arr[maxn], q[maxn];

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    {
        cin >> arr[i];
        arr[i] += arr[i - 1]; //前缀和
    }

    q[1] = 0;
    int l = 1, r = 1, ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        while (l <= r && q[l] < i - m)
            ++l; //把个数超过m的出队

        ans = max(ans, arr[i] - arr[q[l]]);

        while (l <= r && arr[q[r]] >= arr[i])
            --r; //把一定不是最优解的出队
        q[++r] = i;
    }
    cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值