您的位置:首页 > 其它

最大子序列和(枚举,分治,DP)

2017-02-24 16:21 281 查看
零、最大连续子序列和

一个N个整数的序列记作{A1,A2,······,AN},求函数

的最大值。如果最大的子序列和为负数,那么最大子序列和就认为是0。

例如序列{-3,4,-8,10,3,-4,7},其最大连续子序列为{10,3,-4,7},最大连续子序列和为16。

一、枚举

把所有子列和都找出来,比较暴力哈。

代码示例:

int MaxSubseqSum1(int A[],int N)
{
int ThisSum,MaxSum=0;
int i,j,k;
for(i=0;iMaxSum) //更新结果
MaxSum=ThisSum;
}
}
return MaxSum;
}


时间复杂度为O(n^3)

二、枚举优化

仔细观察可以发现,先确定序列的左右端的所有情况再求和可以简化为:先确定左端起点(一个循环)对于右边每个点(第二个循环),每向右一个点就求一次和并更新最大和,便能起到相同的效果并优化。

代码示例:

int MaxSubseqSum2(int A[],int N)
{
int ThisSum,MaxSum=0;
int i,j;
for(i=0;iMaxSum) MaxSum=ThisSum;
}
}
return MaxSum;
}


时间复杂度为O(n^2)

三、分治思想

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序的算法,简单问题可用二分法完成。

最大子序列和也可以用分治的思想来解决。对于一个序列,我们可以先从中间分开,

再递归解决左右两边的问题,递归地解决左边的序列能得到左边的最大子列和;递归地解决右边的序列能得到右边的最大子列和。但是问题还没有结束,跨越边界的序列也可能出现最大子列和。将这三个数找到后,最大的数即为最大子列和了。分而治之的思想~

举例:



如上面这个序列{4,-3,5,-2,-1,2,6,-2},分治后的“最小单元”有四个{4,-3},{5,-2},{-1,2},{6,-2} 比较之后可以得到 4,5,2,6

然后两边各有两个橘黄色的次边界,跨左边界有最大和6,跨右边界有最大和8,最后,跨最后一层的大红色边界最大序列和能找到11,11大于6和8,因此这个序列的最大子列和为11。

代码示例:

int Max(int a,int b,int c)
{
if(a>b&&a>c) return a;
else if(b>a&&b>c) return b;
else return c;
}

int MaxSubSum(int A[],int left,int right)
{
int MaxLeftSum,MaxRightSum;
int MaxLeftBorderSum,MaxRightBorderSum;
int LeftBorderSum,RightBorderSum;
int mid,i;

if(left==right){
if(A[left]>0) return A[left];
else return 0;
}

mid=(right+left)/2;
MaxLeftSum=MaxSubSum(A,left,mid);
MaxRightSum=MaxSubSum(A,mid+1,right);

MaxLeftBorderSum=0;
LeftBorderSum=0;
for(i=mid;i>=left;--i){
LeftBorderSum+=A[i];
if(LeftBorderSum>MaxLeftBorderSum) MaxLeftBorderSum=LeftBorderSum;
}

MaxRightBorderSum=0;
RightBorderSum=0;
for(i=mid+1;i<=right;++i){
RightBorderSum+=A[i];
if(RightBorderSum>MaxRightBorderSum) MaxRightBorderSum=RightBorderSum;
}
return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
}


时间复杂度分析:

左边一半时间为T(N/2),右边一半时间也为T(N/2),处理边界情况的时间复杂度为O(N)

假设经过k次分治之后N=2^k,最后的时间复杂度为O(NlogN),如下图所示:



四、动态规划思想

动态规划是一种“在线处理”的算法,一般有四个步骤:

①划分阶段。按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。

②确定状态和状态变量。将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。

③确定决策并写出状态转移方程。决策和状态转移有
4000
着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可以写出。而常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。

④寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。

 

在本问题中,我们可以将子序列与其子子序列进行问题分割。

 最后得到的状态转移方程为:            

 MaxSum[i] = Max{ MaxSum[i-1] + A[i], A[i]}

在这里,不必设置数组MaxSum[]。

代码示例:
int MaxSubseqSum(int A[],int N)
{
int maxsum=0,sum=0;
for(int i=0;i<N;++i){
sum+=A[i];
if(sum>maxsum) maxsum=sum;
else if(sum<0) sum=0;
}
return maxsum;
}


在本代码实现中,sum持续更新,同时整个过程,只对数据进行了一次扫描,一旦A[i]被读入处理,它就不再需要被记忆了。时间复杂度为O(N)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: