在这四种解法里,解法一是通法,可以学到规律和知识,做基础之用;解法二在解法一的基础上做改进,锻炼思维;解法三则是大名鼎鼎的分治法,涉及到递归的知识,算是“高效算法设计”的基础;解法四以O(n)的复杂度解出最大连续子序和,一个字,神奇。四种解法循序渐进,效率逐步提高,精妙至极。建议初学者每种都要掌握。
解法一:最普通的解法,定义i循环,代表数组从0到n-1的值,定义j循环,代表从a[i]开始,到a[j]结束。 定义k循环,将从a[i]至a[j]所有的数相加,最后求出最大子序列和。
时间复杂度:O(n^3)
#include
using namespace std;
int main() {
int n; cin>>n; int a[n];
for(int i = 0; i < n; i++) cin>>a[i];
int sum = a[0]; //不能等于0,因为可能是全负序列
int temp = 0;
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++) //二重循环, 从i截止到j遍历
for(int k = i; k <=j; k++) //i-j序列的区间和。
temp += a[k];
sum = max(sum, temp);
}
cout << sum << endl;
return 0;
}
解法二:定义数列S[n],S[i]=a[0]+a[1]+...+a[i]
。 若j>i,则S[j]-S[i-1]=a[j]+a[j-1]+...+a[i]
。也就是说,嵌套两个for循环, 一个为i,一个为j,就可以表示出任何子序列的和,最后作比较即可。
时间复杂度:O(n^2)
#include
using namespace std;
int main() {
int n; cin>>n; int a[n];
for(int i = 0; i < n; i++) cin>>a[i];
int S[n+1]; S[0]=0;
for(int i = 1; i <= n; i++) S[i] = a[i] + S[i-1];
int best = 0;
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++) best = max(best, S[j]-S[i-1]);
return 0;}
解法三:分治法,顾名思义,分而治之。
分治算法一般分为以下3个步骤:
一、划分问题:把问题的实例划分成子问题。
二、递归求解:递归解决子问题。
三、合并问题:合并子问题的解得到原问题的解。
最大连续和的分治算法:
1、把序列分成元素个数尽量相等的两半
2、分别求出完全位于左半或者完全位于右半的最佳序列
3、求出起点位于左半、终点位于右半的最大连续和序列,并和子问题的最优解比较。
涉及到递归的代码都比较复杂,但如果实在理解不了,就背下来把,想想小时候背的古诗,就算死记硬背下来,随着时间的推移,也会慢慢懂得其含义的。
时间复杂度:O(nlogn) (这种级别的时间复杂度,一般就可以应付绝大多数的竞赛了)
#include
using namespace std;
int a[10005];
int longsub(int *a, int l, int r) { //返回数组在左闭右开区间[x,y)中最大连续和
//为什么不返回a[r]呢? 因为该区间是左闭右开区间
if(r-l == 1) return a[l]; //只有一个元素直接返回
int mid = l + (r-l)/2; //分治第一步:划分成[l,m)和[m,r)
int maxs = max(longsub(a,l,mid), longsub(a,mid,r)); //分治第二步:递归求解
int v, Lmax, Rmax;
v=0; Lmax = a[mid-1]; //分治第三步:合并(1)——从分界点开始往左的最大连续和l
for(int i=mid-1; i>=l; i--) Lmax=max(Lmax, v+=a[i]);
v=0; Rmax=a[mid]; //分治第三步:合并(2)——从分界点开始往右的最大连续和r
for(int i=mid; i<r; i++) Rmax=max(Rmax, v+=a[i]);
return max(maxs, Lmax+Rmax);
}
int main() {
int n; cin>>n;
for(int i = 0; i < n; i++) cin>>a[i];
cout << longsub(a, 0, n);
return 0;}
解法四:
很特殊的解法,核心思想:当遍历到第i个元素时,判断在它前面的连续子序列和是否大于0,如果大于0,则以位置i结尾的最大连续子序列和为元素i和前门的连续子序列和相加;否则,则以位置i结尾的最大连续子序列和为元素i。
时间复杂度:O(n)
#include
using namespace std;
int main() {
int n; cin>>n; int a[n];
for(int i = 0; i < n; i++) cin>>a[i];
int maxsum, maxhere;
maxsum = maxhere = a[0]; //初始化最大和为a[0]
for(int i = 1; i < n; i++) {
if(maxhere <= 0) maxhere = a[i]; //若前面位置连续子序列和小于0,则以当前位置开始
else maxhere += a[i]; //若大于零,则加入
if(maxhere > maxsum) maxsum = maxhere; //更新
}
cout << maxsum << endl;
return 0;
}
如果这篇文章对你产生了帮助,就请给博主一个赞吧!大家的点赞是我创作的最大动力!