一.最大子段和
给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。
要求算法的时间复杂度为O(n)。
输入格式:
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。
输出格式:
输出最大子段和。
输入样例:
在这里给出一组输入。例如:
6 -2 11 -4 13 -5 -2
输出样例:
在这里给出相应的输出。例如:
20
在这道题中,我采用的方法是设置两个数组,输入数组b[6],即b[6] = -2和累加数组a[6],即a[6] = -2+11-4+13-5-2,如果输入数组里面的所有数字都小于零,则输出输入数组0。否则就利用累加数组,利用累加数组中的最大值减去最小值(必须比0小,如果无,则置零),如此便可以得到相应的输出。
该方法的时间复杂度为O(n);
但是同样也可以使用分治算法,进行解答,我们将规模为n的序列,不断地分为k个小序列,进行排序或者是计算。
在这个题目中,我们将序列分为两个小序列,那么最大字段和就存在三种可能性,1.左边序列2.右边序列3.中间字段
即利用函数int max(int left, int right)返回最大值。利用递归函数,将数组先进行二分,左序列和右序列,当序列中只有一个数的时候则返回,然后通过循环累加计算每个序列的最值
#include<iostream>
using namespace std;
int a[10001];
int maxsum(int left, int right) {
if (left >= right) {
return max(0,a[left]);
} //如果只有1个数
int mid = (left + right) / 2;
int leftSum = maxsum(left, mid);//左边的最大值
int rightSum = maxsum(mid + 1, right);//右边的最大值
int s1 = 0;
int lefts = 0;
for (int i = mid; i >= left; i--) {
lefts += a[i];
if (lefts > s1) {
s1 = lefts;//算出最大值
}
}
int s2 = 0;
int rights = 0;
for (int i = mid + 1; i <= right; i++) {
rights += a[i];
if (rights > s1) {
s1 = rights;
}
}
int sum = s2 + s1 ;
sum = max(sum, leftSum);
sum = max(sum, rightSum);
return sum;
}
int main()
{
int n;
cin >> n;
for(int i=0; i < n; i++)
{
cin >> a[i];
}
cout << maxsum(0,n);
}
时间复杂度O(n) = 2O(n) + O(n)=logn;
分治算法,将一个问题不断地下分成多个子问题,在分别解决,如果分成的子问题还是比较大,可以继续细分,这样能够有效的降低问题的时间复杂度。