写在前面,有个疑问,这种算法好像数组长度只能是偶数,但实际是奇数也可以,没想明白,改天在考虑。
第一个自己写的分治法的题,有点小激动,hoho~~
思路:
分治,分治,一个是分:不断将大问题分解为两个小问题,一直分解,直到解决这个问题比原来要轻松很多。
一个是治,将两个子问题合起来。
先将数组一分为二,求出左半部分的最大子序列和,求出右半部分的最大子序列和,最后求出横跨中间的最大子序列和,比较一下,输出大的那个。
一直分下去,直到只有一个元素,这时就不用计算中间了,直接判断这个数是否大于0,是返回这个数,不是就返回0(0就是一个也不选,空集也是子序列嘛),这样就把一个O(n^2)的问题(不考虑在线法)转化为O(n)的问题了,注意这里只是分,分治之后时间复杂度是O(NlogN)。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
int Max3(int i,int j,int k){
int a[3];
a[0]=i;
a[1]=j;
a[2]=k;
sort(a,a+3);
return a[2];
}
int MaxSubSum(int *a, int left, int right){
int LeftMaxSum,RightMaxSum;//存放左边、右边最大和
int MaxLeftBorderSum=0,MaxRightBorderSum=0;//存放中间向左最大和,存放中间往右最大和
int LeftBorderSum=0,RightBorderSum=0;//临时记录从中间向左、向右的和
if(left == right){//只有一个元素的情况
if(a[left]>0)
return a[left];
else
return 0;
}
int center = (left+right)/2;
LeftMaxSum = MaxSubSum(a,left,center);
RightMaxSum = MaxSubSum(a,center+1,right);
//横跨两边的最大和
for(int i = center;i>=left;i--){
LeftBorderSum += a[i];
if(LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
for(int i = center+1;i<=right;i++){
RightBorderSum += a[i];
if(RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
return Max3(LeftMaxSum,RightMaxSum,MaxLeftBorderSum+MaxRightBorderSum);
}
int main(){
int a[] = {4,-3,5,-2,-1,2,6,-2};
cout << MaxSubSum(a,0,7);
return 0;
}