循环数组求最大子段和
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.
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).
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;
}