最大连续子序列问题(含环路)

Prob III. More than MSP

In computer science, the maximum subarray problem is the task of finding the contiguous subarray within a one-dimensional array of numbers which has the largest sum. For example, for the sequence of values −2, 1, −3, 4, −1, 2, 1, −5, 4; the contiguous subarray with the largest sum is 4, −1, 2, 1, with sum 6.

Now you are to solve this problem under a new restraint: the subarray you are to find shall be with a length not greater than k. For example, for the sequence of values −2, 1, −3, 4, −1, 2, 1, −5, 4 and k equals 3; the contiguous subarray with the largest sum is 4, −1, 2, with sum 5.

You are given a integer sequence A with length of n, and integer k. You are required to find the subarray with length not greater than k and the maximum sum.

Input

There are multiple test cases. Each case contains n+2 integers. The first two integers are n and k. n integers follow giving the sequence A. Not all elements of A are negative. The input is terminated by EOF.

Output

For each case, output the subscripts of the first and the last elements of the subarray you found. If more than one solution exist, output the first one.

Restraints

There are 10 test cases.

1<=k<=n<=10^5

For all elements a of A, -2^31<=a<2^31.

Sample

InputOutput
5 2 2 -5 2 -3 5
5 2 2 5 2 3 5
4 4
3 4

In the first case, the subarray you found is 5, so you output 4 4.

In the second case, the subarray you found is 3, 5, so you output 3 4.


这道题目是3.16日周赛的题目,下面是我看完标程之后自己写的代码(代码后面附加了10组测试数据以及它的结果):


#include<iostream>
#include<vector>
#include<queue>
using namespace std;

int main()
{
    int T;
    cin>>T;
    while(T--){
        int n, k;
        cin>>n>>k;
        vector<long long> a(n+k);
        for(int i = 1; i <= n; i++){//将序列的元素存放到可变数组vector中,注意没有用a[0]存储元素
            cin>>a[i];
            a[i] = a[i] + a[i-1];
        }
        for(int i = n+1; i < n+k; i++)//将序列构成环
            a[i] = a[i-n] + a[n];


        deque<short> d;//定义一个双向队列用来存放数组元素的下标
        d.push_back(0);
        short res0 = 0, res1 = 1;
        for(int i = 1; i < n+k; i++){
            short first = d.front();
            if((a[i] - a[first] > a[res1] - a[res0]) || ((a[i] - a[first] == a[res0] - a[res1]) &&
            ((first < res0) || (first == res0 && i < res1)))){//判断能否对连续最大和子序列的两个端点进行更新
                 res0 = first;
                 res1 = i;
            }

            while(!d.empty() && a[d.back()] > a[i])
                d.pop_back();//如果从队列末尾取出的值作为数组a[]的下标,得到的在数组a[]中的值比a[i]大,就将队尾元素删除

            d.push_back(i);
            if(i - d.front() + 1 > k)
                d.pop_front();
        }

        cout<<(res0%n)<<' '<<(res1-1)%n<<endl;
    }
    return 0;
}
/*
10
10 2
-533 -666 -500 169 724 478 358 -38 -536 705
4 5
10 6
281 -173 961 -509 -5 942 -173 436 -609 -396
2 7
10 3
-847 -708 -618 421 -284 718 895 447 726 -229
6 8
10 9
869 912 667 -701 35 894 -297 811 322 -667
4 2
10 4
-336 141 711 -747 -132 547 644 -338 -243 -963
5 6
10 10
-277 741 529 -222 -684 35 -810 842 -712 -894
1 2
10 1
-58 264 -352 446 805 890 -271 -630 350 6
5 5
10 2
-607 548 629 -377 -916 954 -244 840 -34 376
1 2
10 2
-692 -56 -561 -374 323 537 538 -882 -918 -71
5 6
10 2
-167 115 -361 658 -296 930 977 -694 673 -614
5 6

Process returned 0 (0x0)   execution time : 4.196 s
Press any key to continue.
*/

思想:

首先

队首d.front()存放的是队列中最小的元素,队尾存放的是最大的元素(也就是循环当中的a[i]),这样两个数想减得到的就是对于点 i 来说满足长度不超过k的最大连续和


解释一下我的代码:


1.使用了STL当中的双向队列deque,以及可变长度的数组vector.

2.第一个for()循环将输入的序列元素存放在数组a[ ]中,并使得a[i] 为从点0到点 i 的序列和,第二个for()循环就是使首尾相连构成环路(这里大家仔细想想为什么那样表示)。

3.注意双向队列中存放的是数组a[ ]的下标,开始时将下标0放到队列中(d.push_back())。

4.利用一个for()循环检查当移动到下标为i时能否对res0和res1进行更新,更新的条件有:(a[i] - a[first] > a[res1] - a[res0]) ,也就是说找到了一个更大的max满足题目要求;((a[i] - a[first] == a[res0] - a[res1]) && ((first < res0) || (first == res0 && i < res1))),即使没有办法得到更大的连续和,但是由于题目中要求当存在多个结果时输出第一个结果。于是当max不变时,如果起始点下标变小了,或者起始下标不变但是终止下标变小了,这两种情况也可以对最大和的联系子序列的起始点和终止点进行更新。

5.把队尾中的元素作为数组a[ ]的下标的时候如果a[d.back()] < a[i]时,那么就把其移出队列。

6.如果此时队头元素的值已经不在要检查的长度<=k的范围内,就移出队列。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值