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

来源:互联网 发布:重庆仙桃数据有限公司 编辑:程序博客网 时间:2024/05/16 18:10

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 is4, −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 thank. For example, for the sequence of values −2, 1, −3, 4, −1, 2, 1, −5, 4 andk equals 3; the contiguous subarray with the largest sum is4, −1, 2, with sum 5.

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

Input

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

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 55 2 2 5 2 3 5
4 43 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 output3 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;}/*1010 2-533 -666 -500 169 724 478 358 -38 -536 7054 510 6281 -173 961 -509 -5 942 -173 436 -609 -3962 710 3-847 -708 -618 421 -284 718 895 447 726 -2296 810 9869 912 667 -701 35 894 -297 811 322 -6674 210 4-336 141 711 -747 -132 547 644 -338 -243 -9635 610 10-277 741 529 -222 -684 35 -810 842 -712 -8941 210 1-58 264 -352 446 805 890 -271 -630 350 65 510 2-607 548 629 -377 -916 954 -244 840 -34 3761 210 2-692 -56 -561 -374 323 537 538 -882 -918 -715 610 2-167 115 -361 658 -296 930 977 -694 673 -6145 6Process returned 0 (0x0)   execution time : 4.196 sPress 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
原创粉丝点击