环形最大子段和

循环数组求最大子段和

N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1],a[n],a[1],a[2]这样的序列)。当所给的整数均为负数时和为0。

例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。

Input 第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)

Output 输出循环数组的最大子段和。

Sample Input

6 -2 11 -4 13 -5 -2

Sample Output

20 

方法一:用前缀和和动态转移方程求解

    思路:输入ai数组,bi=-ai;

       将数组看成一个环状,那么最大子段和可能位于序列之中,也可能是序列的前缀和末尾之和;

       求出最大子段和,最小子段和(无论正负皆可)---->序列和减去最小子段和即为序列的前缀和末尾最大和;

       比较两个子段和输出最大值。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=5e4+10;
typedef long long ll;
ll a[maxn],b[maxn],dp[maxn];
int main() {
    int n;scanf("%d",&n);
    ll sum=0,Max=0,Min=0,ans=0;
    for(int i=1;i<=n;i++) {
        scanf("%lld",&a[i]);
        sum+=a[i];
        b[i]=-a[i];
    }
    for(int i=1;i<=n;i++) {
        dp[i]=max(dp[i-1]+a[i],a[i]);
        Max=max(Max,dp[i]);
    }
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++) {
        dp[i]=max(dp[i-1]+b[i],b[i]);
        Min=max(Min,dp[i]);
    }
    ans=max(Max,sum+Min);
    printf("%lld",ans);
    return 0;
}

方法二:双端队列求解(限制长度为n)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=5e4*2+10;
typedef long long ll;
ll a[maxn*2],sum[maxn*2];
int main() {
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=n+1;i<=n+n;i++) sum[i]=sum[i-1]+a[i-n];
    ll ans=-0x3f3f3f3f3f3f3f3f;
    deque<int> q;
    for(int i=1;i<n+n;i++) {
        while(!q.empty()&&sum[i-1]<sum[q.back()]) q.pop_back();
        while(!q.empty()&&i-q.front()>n) q.pop_front();
        q.push_back(i-1);
        if(sum[i]-sum[q.front()]>ans) ans=sum[i]-sum[q.front()];
    }
    printf("%lld",ans);
    return 0;
}

Max Sum of Max-K-sub-sequence(限制长度为k)

Given a circle sequence A[1],A[2],A[3]......A[n]. Circle sequence means the left neighbour of A[1] is A[n] , and the right neighbour of A[n] is A[1].
Now your job is to calculate the max sum of a Max-K-sub-sequence. Max-K-sub-sequence means a continuous non-empty sub-sequence which length not exceed K.

 Input

The first line of the input contains an integer T(1<=T<=100) which means the number of test cases.
Then T lines follow, each line starts with two integers N , K(1<=N<=100000 , 1<=K<=N), then N integers followed(all the integers are between -1000 and 1000).
Output
For each test case, you should output a line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the minimum start position, if still more than one , output the minimum length of them.Sample Input
4
6 3
6 -1 2 -6 5 -5
6 4
6 -1 2 -6 5 -5
6 3
-1 2 -6 5 -5 6
6 6
-1 -1 -1 -1 -1 -1
Sample Output
7 1 3
7 1 3
7 6 2
-1 1 1

思路:同上一题的法二一样,可以用双端队列求解; 
只不过把限制长度由n改为k;
   注意输入多组数据,初始化变量和数组。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=5e4*2+10;
typedef long long ll;
ll a[maxn*2],sum[maxn*2];
int main() {
    int n,T,k,l,r;scanf("%d",&T);
    for(int t=1;t<=T;t++) {
        scanf("%d%d",&n,&k);
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++) {
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=n+1;i<=n+k;i++) sum[i]=sum[i-1]+a[i-n];
        ll ans=-0x3f3f3f3f3f3f3f3f;
        deque<int> q;
        for(int i=1;i<n+k;i++) {
            while(!q.empty()&&sum[i-1]<sum[q.back()]) q.pop_back();
            while(!q.empty()&&i-q.front()>k) q.pop_front();
            q.push_back(i-1);
            if(sum[i]-sum[q.front()]>ans) {
                ans=sum[i]-sum[q.front()];
                l=q.front()+1;
                r=i;
            }
        }
        if(l>n) l=l-n;
        if(r>n) r=r-n;
        printf("%lld %d %d
",ans,l,r);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/1999-09-21Karry-erfs/p/13153017.html