求连续最大子列和的六种算法
方法概述:
算法1:暴力枚举所有连续子列和,算其中最大的。复杂度$O(n^3)$。算法2:为算法1的优化,去掉内层求连续和的循环,在之前遍历数组,预处理出前缀和,方便在\(O(1)\)内算出连续子列和。复杂度\(O(n^2)\)。
算法3:分治法,当前处理区间的连续和等于左边最大连续和与右边连续和与跨区间中点连续和的最大值。递归基为区间长度为一的情况,最大连续和就是本身。复杂度\(O(nlogn)\)。
算法4:动态规划算法,\(dp[n]\)表示以第n个元素结尾的最大子列和,
转移方程为:\(dp[n]=num[n]+max(0,dp[n-1])\),意思是如果\(dp[n-1]\)大于零,则以第n个元素结尾的区间和就接上前面的子列和,如果小于零,就另立门户,说明前面的和不可能是全局最大的了.复杂度\(O(n)\)。
算法5:思想:sum[]数组表示前缀和,\(sum[n]-sum[m-1]\)就是从m到n的子列和。要\(sum[n]\)最大,就要找\(sum[m-1]\)最小。在遍历过程中计算前缀和,更新最小前缀和与子列和的最大值。
算法6:思想:遍历数组,不断计算累加和并更新最大子列和,一旦和小于零,另立门户,继续计算。(注:只适用于有正有负数列,全为负则返回零)
如更详细,参见参考文章。
代码:
统一说明:数列数字从下标\(1\)开始,个别方法会对原数组修改,若不想这样使用时可以稍作修改,比如另设累计变量,但应注意遍历范围有无改变
#include <iostream>
using namespace std;
int n = 6;
int maxsum_1()//暴力枚举
{
int num[7] = {0,-4,3,1,-5,2,7};
int ans = num[1];
for(int i = 1;i<=n;i++)//以i为起点j为终点的子列的和
{
for(int j = i;j<=n;j++)
{
int sum = 0;
for(int k = i;k<=j;k++)
{
sum += num[k];
}
if(sum>ans)
{
ans = sum;
}
}
}
return ans;
}
int maxsum_2()//暴力法前缀和优化
{
int num[7] = {0,-4,3,1,-5,2,7};
int sum[1000];//存储前缀和
sum[0] = 0;
int ans = num[1];
for(int i = 1;i<=n;i++)
{
sum[i] = sum[i-1]+num[i];
}
for(int i = 1;i<=n;i++)
{
for(int j = i;j<=n;j++)
{
if(ans<sum[j]-sum[i])
{
ans = sum[j]-sum[i];
}
}
}
return ans;
}
int maxsum_3(int l,int r)//分治法
{
int num[7] = {0,-4,3,1,-5,2,7};
if(l==r)//递归基
{
return num[l];
}
int mid = (l+r)>>1;
int lans = maxsum_3(l,mid);//递归算左边最大和
int rans = maxsum_3(mid+1,r);//递归算右边最大和
int sum = 0;//处理跨mid的两边的最大和
int lmax = num[mid];
for(int i = mid;i>=l;i--)
{
sum += num[i];
if(sum>lmax)
{
lmax = sum;
}
}
sum = 0;
int rmax = num[mid+1];
for(int i = mid+1;i<=r;i++)
{
sum += num[i];
if(sum>rmax)
{
rmax = sum;
}
}
int ans = lmax+rmax;
ans = max(ans,lans);
ans = max(ans,rans);//在上述三种情况下取最大值
return ans;
}
int maxsum_4()//num[i]表示以第i个数结尾的连续子列的最大和
{
int num[7] = {0,-4,3,1,-5,2,7};
int ans = num[1];
num[0]=0;
for(int i = 1;i<=n;i++)
{
if(num[i-1]>0) num[i] += num[i-1];//如果i-1结尾的连续子列最大和大于零,可直接接上第i个元素,构成以i为结尾的最大和
else num[i]+=0;//如果小于等于零,子列最大和再次中断,另辟蹊径
if(num[i]>ans) //并在同时更新最大值
{
ans = num[i];
}
}
return ans;
}
int maxsum_5()
{
int num[7] = {0,-4,3,1,-5,2,7};
int lmin = 0;
num[0] = 0;
int ans = num[1];
for(int i = 1;i<=n;i++)
{
num[i] += num[i-1];
//cout << " num " << num[i] << endl;
if(num[i]-lmin>ans)//更新最大值
{
ans = num[i]-lmin;
}
if(lmin>num[i])//更新最小前缀和
{
lmin = num[i];
}
}
return ans;
}
int maxsum_5_1()
{
int b;
int ans;
cin >> n >> b;
int s = ans = b;
int m = 0;
for(int i = 1;i<n;i++)
{
if(s<m)
{
m = s;
}
cin >> b;
s += b;
if(s-m>ans)
{
ans = s-m;
}
}
}
int maxsum_6(int num[])//当全为负数时返回的是零,即数列必须有正有负
{
int ans = 0;
int tmp = 0;
for(int i = 1;i<=n;i++)
{
tmp += num[i];
if(tmp<0)
{
tmp = 0;
}
ans = max(ans,tmp);
}
}
int main()
{
cout << maxsum_1() << endl;
cout << maxsum_2() << endl;
cout << maxsum_3(1,6) << endl;
cout << maxsum_4() << endl;
cout << maxsum_5() << endl;
cout << maxsum_6() << endl;
return 0;
}
参考文章:
netcon,经典算法问题 - 最大连续子数列和,https://www.cnblogs.com/conw/p/5896155.html
我们不只生产博客,也是优质博客链接的搬运工