#最大子列和问题 (在N个数的序列中,求其连续的子序列最大和的问题)
“暴力枚举、简化枚举、 分冶法、 在线处理”
##算法1 暴力枚举
直接枚举所有情况直接比较:
#include<iostream>
#include "stdio.h"
#include <math.h>
using namespace std;
//算法 1 时间复杂度是O(n^3)
int MaxSubseqSum1(int A[],int N)
{
int ThisSum,MaxSum=0;
int i,j,k;
for(i=0;i<N;i++){//i是子列左端位置
for(j=1;j<N;j++){//j是子列右端位置
ThisSum=0;//ThisSum是从A[i]到A[j]的子列和
for(k=i;k<j;k++)
ThisSum+=A[k];
if(ThisSum>MaxSum)//如果刚得到的这个子列和更大
MaxSum = ThisSum;//则更新结果
}//j循环结束
}//i循环结束
return MaxSum;
}
##算法2
为了降低复杂度,我们发现发现其实可以通过对于相同的i、不同的j只需要在j-1的基础上累加一项简化枚举即可
//时间复杂度是O(n^2)
int MaxSubseqSum2(int A[],int N)
{
int ThisSum,MaxSum = 0;
int i,j;
for(i=0;i<N;i++){//i是子列的左端
ThisSum = 0;//ThisSum是从A[i]到A[j]的子列和
for(j=i;j<N;j++){//j是子列右端位置
ThisSum +=A[j];
//对于相同的i,不同的j,只要在j-1次循环的基础上累加1项即可
if(ThisSum > MaxSum)//如果刚得到的这个子列和更大
MaxSum = ThisSum;//则更新结果
}//j循环结束
}//i循环结束
return MaxSum;
}
算法3 分冶法
将规模为N的序列划分成足够小的区间,从而产生与原问题相同的子问题,通过对子问题的求解,在解的集合中我们就可以比较得出最大子列和。
为了方便理解,举个例子:
2 -1 4 -2
明显答案当然是2 -1 4 = 5
2 -1
ML 2 MR 0 MLB+MRB=2
返回2 ML
同理
4 0
返回4 MR
2 -1 4 -2
ML 1 MR 4 MLB+MRB=5
返回5 MLB+MRB
最后比较的当然是5 2 4,所以答案是5
//算法3 分而治之 时间复杂度是O(n*log n)
int Max3(int A, int B, int C)//求最大值
{
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer(int left,int right, int List[])
{
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum;
int LeftBorderSum, RightBorderSum;
int center, i;
if (left == right)
{
if (List[left] > 0)
return List[left];
else
return 0;
}
center = (left + right) >> 1;
/*>> 是右移运算,在计算机中是一种运算操作,
但是他的运算结果正好能对应一个整数的二分之一值,
这就正好能代替数学上的除2运算,但是比除2运算要快。*/
MaxLeftSum = DivideAndConquer(left, center, List); //递归求左边最大子列和
MaxRightSum = DivideAndConquer(center+1, right, List); //递归求右边最大子列和
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 MaxSubseqSum3(int N[],int K)
{
return DivideAndConquer(0, K-1, N);
}
#算法4 在线处理
由算法3我们已经明显降低了时间复杂度,但是从跨越子列的最大子列和的求法中我们又发现了端倪,如果我们不断实时更新最大子列和,那么我们其实只需要对整个数列从左到右进行扫描即可得到整个数列的最大子列和。
//算法4 在线处理
int MaxSubseqSum4(int A[],int N)
{
int i;
int ThisSum,MaxSum;
ThisSum=MaxSum=0;
for(i=0;i<N;i++){
ThisSum +=A[i];//向右累加
if(ThisSum>MaxSum)
MaxSum = ThisSum;//发现更大和则更新当前结果
else if(ThisSum<0)//如果当前子列和为负
ThisSum =0;// 则不可能使后面的部分和增大,则弃之
}
return MaxSum;
}
int main()
{
int i, K;
cin >> K;
int *A = new int[K];
int Max;
for (i = 0; i < K; i++)
cin >> A[i];
Max = MaxSubseqSum3(A,K);
cout << Max<< endl;
return 0;
}
从第一种算法到第四种算法的演变,算法越来越有效率了啊,还有其中分冶法的分而治之思想和在线处理的理念特别值得我们认真思考!(码字不易,点个赞再走呗!)