最大连续子列和
题目:
给定K个整数组成的序列{ N1 , N2 , …, NK },“连续子列”被定义为{Ni , Ni+1 , …, Nj },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:
数据1:与样例等价,测试基本正确性;
数据2:102个随机整数;
数据3:103个随机整数;
数据4:104个随机整数;
数据5:105个随机整数;
输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。
输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。
输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20
类似题目:杭电oj–1003
以下是网上搜集的各种解法,亲测有效
解法一:暴力:三重循环,枚举所有可能的连续子列,复杂度T(n^3)
解法二:暴力基础上升级,减少部分重复计算
解法三:分治法。二分,计算左右两侧的最大连续子列和、跨越中线的最大连续子列和,三者取最大值。但不方便求出最大连续子列的开始和结束下标。
解法四:最优解法,第一次遍历求出以每个元素结尾的最大子序列和并保存到该数组元素中,第二次便利找出开始和结束的下标(输入输出格式匹配杭电oj–1003)。
long long int method1 (int arr[],int n)//传入数组和数组长度
{
long long int ThisSum=0,MaxSum=0;
for(int i=0;i<n;i++)
{
for(int j=i;j<n;j++)
{
ThisSum=0;
for(int k=i;k<=j;k++)
ThisSum+=arr[k];
if(ThisSum>MaxSum)
MaxSum=ThisSum;
}
}
return MaxSum;
}
long long int method2(int arr[],int n)
{
long long int ThisSum=0,MaxSum=0;
for(int i=0;i<n;i++)
{
ThisSum=0;
for(int j=i;j<n;j++)
{
ThisSum+=arr[j];
if(ThisSum>MaxSum)
MaxSum=ThisSum;
}
}
return MaxSum;
}
long long int method3(int arr[],int n)//保持相同的函数接口
{
return DivideAndConquer(arr,0,n-1);
}
long long int DivideAndConquer (int arr[],int low,int last)//计算下标从low到last的数组的最大子列和
{
if(last==low)//数组长度为1时,返回元素值或0
{
if(arr[low]>0)
return arr[low];
else
return 0;
}
//分治
int mid=(low+last)/2;
long long int MaxSum=0,ThisSum=0,MaxLeftBorderSum=0,MaxRightBorderSum=0;//最大和、当前子列和、跨界向左最大和、跨界向右最大和
MaxSum=max(DivideAndConquer(arr,low,mid),DivideAndConquer(arr,mid+1,last));//分别计算左右两个子列的最大子列和并保留较大值
//计算跨界子列的最大和
for(int i=mid;i>=low;i--)//从中向左扫描
{
ThisSum+=arr[i];
if(ThisSum>MaxLeftBorderSum)
MaxLeftBorderSum=ThisSum;
}
ThisSum=0;
for(int i=mid+1;i<=last;i++)//从中向右扫描
{
ThisSum+=arr[i];
if(ThisSum>MaxRightBorderSum)
MaxRightBorderSum=ThisSum;
}
MaxSum=max(MaxLeftBorderSum+MaxRightBorderSum,MaxSum);
return MaxSum;
}
``
//解法4
#include <iostream>
using namespace std;
int max(int a,int b){ return a>b?a:b; }
int main(){
int T,N;
cin >> T;
for(int i=0;i<T;i++){
int f[100001],start=0,end=0,big=-1000;
cin >> N;
cin >> f[0];
big = max(f[0],big);
for(int j=1;j<N;j++){
cin >> f[j];
//求和
int sum = f[j-1] + f[j] ;
//如果 f[j-1] + f[j] 的和不大于 f[j] 的本身,那么视为减益 ,即以f[j-1]结尾的最大子序列和是负数,因此f[j]作为子序列首重新开始计算,而不加上f[j-1]。
if(sum >= f[j] && f[j-1] + f[j] >= 0){
f[j] = sum;
}
big = max(f[j],big);
}
//寻找开始和结束的下标
// f[j] 小于0则视为增益区间结束
// f[j] 大于0则为增益区间
for(int j=0;j<N;j++){
if(f[j]==big){
end = j;
break;
}
if(f[j]<0) start=j+1;
}
cout << "Case " << i+1 << ':' << endl;
cout << big << ' ' << start+1 << ' ' << end+1 <<endl;
if(i<T-1)cout <<endl;
}
return 0;
}`
//解法4另一种形式
#include<stdio.h>
#include<malloc.h>
#include<string.h>
void main(){
int T, N;
int t, i;
int a[100001];
scanf("%d", &T);
for(t = 0; t < T; t++){
scanf("%d", &N);
for(i = 0; i < N; i++)
scanf("%d", &a[i]);
int maxSum = a[0];
int sum = 0, l=0, r=0;
int tmpL = 0;
for(i = 0; i < N; i++){
sum += a[i];
if(sum > maxSum){
maxSum = sum;
l = tmpL;
r = i;
}
if(sum < 0){
sum = 0;
tmpL = i+1;
}
}
printf("Case %d:\n", t+1);
printf("%d %d %d\n", maxSum, l+1, r+1);
if(t < T-1)
printf("\n");
}
}
`