所谓最大连续子序列,就是求出给定一串数字中连续的那一段元素和的最大值。
举例如:
给定序列:2 3 -1 4 7 则最大子序列的值应为4+7=11.
先说第一种方法:暴力枚举数组中的元素,不断求和,进行更新即可。可是时间复杂度上一般承受不了。O(n^3)的时间复杂度确实对一般的问题很难接受。
第二种:把a[0]初始化为max值,则枚举n个位置,每次把sum置为0,然后从位置i开始计算从i开始的最大连续子序列和的大小,如果大于max,则更新max。
部分代码如下:
inline int maxsequence(int arr[], int len)
{
int max = arr[0]; //初始化最大值为第一个元素
for (int i=0; i<len; i++) {
int sum = 0; //sum必须清零
for (int j=i; j<len; j++) { //从位置i开始计算从i开始的最大连续子序列和的大小,如果大于max,则更新max。
sum += arr[j];
if (sum > max)
max = sum;
}
}
return max;
}
第三种:分治法。原序列可以分为两部分,不断递归计算出这两块序列的和,然后从中间向两端遍历,找到最大的序列的和。
部分代码如下:
/*求三个数最大值*/
inline int max3(int i, int j, int k)
{
if (i>=j && i>=k)
return i;
return max3(j, k, i);
}
inline int maxsequence2(int a[], int l, int u)
{
if (l > u) return 0;
if (l == u) return a[l];
int m = (l + u) / 2;
/*求横跨左右的最大连续子序列左半部分*/
int lmax=a[m], lsum=0;
for (int i=m; i>=l; i--)
{
lsum += a[i];
if (lsum > lmax)
lmax = lsum;
}
/*求横跨左右的最大连续子序列右半部分*/
int rmax=a[m+1], rsum = 0;
for (int i=m+1; i<=u; i++)
{
rsum += a[i];
if (rsum > rmax)
rmax = rsum;
}
return max3(lmax+rmax, maxsequence2(a, l, m), maxsequence2(a, m+1, u)); //返回三者最大值
}
第四种:读入的时候预处理出一个前缀和数组,即sum[i]表示a[o]+a[1]+a[2]+.....+a[i-1];
然后又是枚举了,但实际这个复杂度也偏高,效率不太理想。
第五种:DP,最简单的DP吧,这个不知道DP的人也可以很容易写出来,用dp[i]表示以a[i]结尾所获得的最大值,那么显然有:dp[i]=max(dp[i-1]+a[i],a[i]);不断更新这个dp数组即可,时间复杂度最优,O(n)。
另外值得一提的是,还有一种变形的最大连续子序列的问题,简单的如这个题的B题
最常见的也是稍微有点麻烦的就是求最大子矩阵的问题,以前一直不会算,最后稍微想下就明白了。给出的是二维数组,但是求的值相当于还是应用的最大连续子序列的思想。具体做法就是把矩阵转换为一维的来算。
也就是枚举矩阵的连续几行的合并,这样就转换为一维的了,再用最大子序列的算法去求,更新最大值就可以了
下面附上几种求连续最大子序列的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
int dp[501][501];
using namespace std;
inline int maxsequence(int arr[], int len)
{
int max = arr[0]; //初始化最大值为第一个元素
for (int i=0; i<len; i++) {
int sum = 0; //sum必须清零
for (int j=i; j<len; j++) { //从位置i开始计算从i开始的最大连续子序列和的大小,如果大于max,则更新max。
sum += arr[j];
if (sum > max)
max = sum;
}
}
return max;
}
/*求三个数最大值*/
inline int max3(int i, int j, int k)
{
if (i>=j && i>=k)
return i;
return max3(j, k, i);
}
inline int maxsequence2(int a[], int l, int u)
{
if (l > u) return 0;
if (l == u) return a[l];
int m = (l + u) / 2;
/*求横跨左右的最大连续子序列左半部分*/
int lmax=a[m], lsum=0;
for (int i=m; i>=l; i--)
{
lsum += a[i];
if (lsum > lmax)
lmax = lsum;
}
/*求横跨左右的最大连续子序列右半部分*/
int rmax=a[m+1], rsum = 0;
for (int i=m+1; i<=u; i++)
{
rsum += a[i];
if (rsum > rmax)
rmax = rsum;
}
return max3(lmax+rmax, maxsequence2(a, l, m), maxsequence2(a, m+1, u)); //返回三者最大值
}
int maxsequence3(int a[], int len)
{
int maxsum, maxhere;
maxsum = maxhere = a[0]; //初始化最大和为a【0】
for (int i=1; i<len; i++) {
if (maxhere <= 0)
maxhere = a[i]; //如果前面位置最大连续子序列和小于等于0,则以当前位置i结尾的最大连续子序列和为a[i]
else
maxhere += a[i]; //如果前面位置最大连续子序列和大于0,则以当前位置i结尾的最大连续子序列和为它们两者之和
if (maxhere > maxsum) {
maxsum = maxhere; //更新最大连续子序列和
}
}
return maxsum;
}
int main()
{
int a[100];
int n;
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>a[i];
}
cout<<maxsequence3(a,n)<<endl;
}
return 0;
}