最大子列和问题,一共有四种方法来解决。
1.方法及复杂度:
方法 | 复杂度 |
---|---|
暴力算法 | O(N^3) |
轻度暴力算法 | O(N^2) |
分而治之算法 | O(NlogN) |
在线处理 | O(N) |
2.
3.“在线”的意思是指每输入一个数据就进行即时处理,在任何一个地方中止输入,算法都能正确给出当前的解。
4.修改一下第二种方法可以用来解决PAT 1007
这个题当时做的时候还是考虑的不周全,忽略了负数和0交替的情况
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
/*最简单的一种方法,在线处理方法,时间复杂度为O(N)*/
int MaxArraySum1(int k ,int a[]){
int ThisSum,MaxSum = 0;
int start,end;
for(int i =0; i < k;i++){
ThisSum += a[i];
if(ThisSum > MaxSum)//如果当前和大于目前最大值,取代
MaxSum = ThisSum;
if(ThisSum < 0)//如果当前值小于0,那么他一定不会让后面加上的数更大,所以舍弃这部分数
ThisSum = 0;
}
return MaxSum;
}
int Max3( int A, int B, int C )
{ /* 返回3个整数中的最大值 */
return A > B ? A > C ? A : C : B > C ? B : C;
}
/*分而治之算法,时间复杂度为O(NlogN)*/
int DivideAndConquer(int List[],int left,int right){
int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/
int LeftBorderSum, RightBorderSum;
int center, i;
if( left == right ) { /* 递归的终止条件,子列只有1个数字 */
if( List[left] > 0 ) return List[left];
else return 0;
}
center = (left+right)/2;
/* 递归求得两边子列的最大和 */
MaxLeftSum = DivideAndConquer( List, left, center );
MaxRightSum = DivideAndConquer( List, center+1, right );
/* 下面求跨分界线的最大子列和 */
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for( i=center; i>=left; i-- ) { /* 从中线向左扫描 */
LeftBorderSum += List[i];
if( LeftBorderSum > MaxLeftBorderSum )
MaxLeftBorderSum = LeftBorderSum;
} /* 左边扫描结束 */
MaxRightBorderSum = 0; RightBorderSum = 0;
for( i=center+1; i<=right; i++ ) { /* 从中线向右扫描 */
RightBorderSum += List[i];
if( RightBorderSum > MaxRightBorderSum )
MaxRightBorderSum = RightBorderSum;
} /* 右边扫描结束 */
/* 下面返回"治"的结果 */
return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}
int MaxArraySum2(int k ,int a[]){
return DivideAndConquer( a, 0, k-1 );
}
/*如果要求找出起始位置和终止位置,用O(n^2)的方法比较好*/
int* MaxArraySum3(int k,int a[]){
int CurrentSum,MaxSum=0;
int left,right;
int count = 0;
for(int i = 0;i < k;i++){
CurrentSum = 0;
if(a[i] < 0)
count++;
for(int j = i;j < k;j++){
CurrentSum += a[j];
if(CurrentSum > MaxSum){//如果当前和大于最大值,取代
MaxSum = CurrentSum;
left = a[i];
right = a[j];
}
if(CurrentSum == 0&& MaxSum == 0){//注意这种情况可以处理负数和0交替的情况,eg:5 -1 0 -1 0 -1
MaxSum = CurrentSum;
left = a[i];
right = a[j];
}
}
}
int* result = new int[3];
if(count == k){//如果输入的数都是负数
result[0] = 0;
result[1] = a[0];
result[2] = a[k-1];
}
else{
result[0] = MaxSum;
result[1] = left;
result[2] = right;
}
return result;//返回数组要返回一个指针
}
int main(int argc,char *argv[]){
int k;
scanf("%d",&k);
int a[100000];
for(int i = 0;i < k;i++){
cin >> a[i];
}
int result;
result = MaxArraySum2(k,a);
printf("%d" , result);
/*
int* b;
b = MaxArraySum3(k,a);
for(int i = 0; i < 2;i++)
printf("%d " , b[i]);
printf("%d" , b[2]);
//delete[] b;
return 0;
*/
}