求最大子列和(Maximum subsequence sum)

it2025-02-11  27

例题:最大子序列和(Maximum subsequence sum) 题意:给定一个序列,找到连续序列的和为最大的序列之和。

方法一:在线处理法 < 最快方法,时间复杂度为O(N)>

思路: 在读入数据的同时,进行求和计算,利用一个变量Maxsum 来保存上一个所计算的和, 设置另一个变量Thissum来储存持续相加得到的和,通过比较这两个变量,来决定是否更新Maxsum,大于则更新,反之不变,同时判断Thissum是否小于0,小于0则,重置为0.

步骤: 1,输入数组 2,输入数组的同时进行求和运算, 3,持续比较上一次找到的最大和值与此时的最大和值的关系,大于则 更新Maxsum,小于则不变 4,接着判断此时的和值是否小于0,小于则将和值重置为0(和为负数的值全部抛弃)。 5,输出最大和值Maxsum即可。 第四步的原因: 解释:当和出现负数时,若持续进行和运算,只会将后面的数值变得更小,远不如我重新开始求和运算的数值大,因此,只要是前面求和运算为负数,那么就直接抛弃,若序列第一个数值为负也是一样的道理,直接抛弃,Thissum依旧为0,若是相加的运算中出现负数,则在这之前的数值都舍弃不要,重新来过,从0开始。

总结:该算法之所以可以达到O(N)的时间复杂度,就是基于第四步,将算法优化,线性即可解决,相比于下面两个方法,

//代码实现: # include<iostream> # define Max 100010 using namespace std; int main() { //读入该序列长度 int n; cin >> n; //定义一个数组存放该序列 int a[Max]; //初始化为0 int Maxsum = 0, Thissum = 0; for (int i = 0; i < n; i++) { cin >> a[i]; Thissum += a[i]; if (Thissum > Maxsum) { //此时和值大于上一个最大和值时,更新Maxsum的值 Maxsum = Thissum; } else if (Thissum < 0) { //当和值出现小于0的情况时,将此时的和值重置为0 Thissum = 0; } } //以上操作完成后,输出Maxsum即可 cout << Maxsum << endl; }

方法二:分而治之(时间复杂度 O(Nlog(N)) )

思路:利用递归,将该序列不断分成两半,分别在左边,右边,比及跨中间的区间, 接着对半分,直到,左边与右边的值相等时退出这一层的递归,开始比较以及求和运算 查找到每一区间的最大和,然后比较三个区间的和找出最大的那一个。

步骤:1,首先读入,N和一系列数值存入数组 2,写递归出口,当左边的坐标和右坐标相等并且该值大于0时,return 此时的数组值,反之返回0 3,划分区域,找到中间坐标, 4,递归左边(调用自身改变下标即可)利用同样的方式,找到左边最大的和 4,递归右边(同上)利用同样的方式,找到右边最大的和 5,递归中间区间(从中间开始分别向左和向右找到一个和最大的序列(线性查找)), 左右两边的值相加。 6,比较这三个数,那个数最大,返回即可。 代码实现:

# include<iostream> # define Max 100010 using namespace std; //查找三个中的最大值 int cmp(int a, int b, int c) { // return a > b ? a > c ? a : c : b > c ? b : c; //cout << a << " " << b << " " << c << endl; if (a > b) { if (a > c) return a; else return c; } else { if (b > c) return b; else return c; } } int FindMax(int list[], int left, int right) { //出口 if (left == right) { if(list[left]>0) //只返回大于0 的值 return list[left]; else return 0; } int center = (left + right) / 2; //找到中间坐标 int MaxLeft = FindMax(list, left, center); //左边的最大值 int MaxRight = FindMax(list, center + 1, right); //右边的最大值 int MaxLeftMinde = 0; int ThisLeftMinde = 0; //从中间向左边遍历 for (int i = center; i >= left; i--) { ThisLeftMinde += list[i]; if (MaxLeftMinde < ThisLeftMinde) { MaxLeftMinde = ThisLeftMinde; } } int ThisRightMinde = 0; int MaxRightMinde = 0; //从中间向右边遍历 for (int i = center+1; i <= right; i++) { ThisRightMinde += list[i]; if (MaxRightMinde < ThisRightMinde) { MaxRightMinde = ThisRightMinde; } } int MaxMinde = MaxLeftMinde + MaxRightMinde; //cmp(MaxMinde, MaxRight, MaxLeft); return cmp(MaxMinde, MaxRight, MaxLeft); } int main() { //读入该序列长度 int n; cin >> n; //定义一个数组存放该序列 int a[Max]; for (int i = 0; i < n; i++) { cin >> a[i]; } cout << FindMax(a, 0, n - 1); }

总结:分而治之,把问题由大化小,分开处理后,再结合起来,将该问题,划分成找左边,找右边,以及找包含中间的最大值,找到之后,合起来比较大小取最大的,这就是递归一层做的事。找到最左边的一个值开始向上做(左边右边值的个数不断扩大)求和运算。

最新回复(0)