✨最大子序列
<作者 : 丘比特,大梦想 日期 : 2023.3.2>
本文是对一些问题的整理,采用多种算法同时对原理和机制进行分析的问题记录贴
题目内容(简略介绍)
就是在一段正负相间的数列中找到和最大的那个序列以及,返回对应的和,注意这里的最大是指的和0相比较所以一旦临时和达到了负数的时候直接这步不往下计算了即可,进行对应的舍弃
思路一(暴力枚举)
简单来讲就是将起点不断变化,使得得到不同起点的子数列,根据在临时和加上不同多的数来表示同个起点的不同子数列,在得到不同和
和当前最大和进行比较即可
代码实现:
int Maxsum(int * p,int sz)
{
int nsum = 0;
int maxsum = 0;
//外层循环任务是不断对起点进行调整
for(int i = 0;i < sz;i++)
{
//不断从定好的起点进行调整,枚举子串
for(int j = i;j < sz;j++)
{
nsum = 0;
//特别蠢的做法就是不断重置不断计算
for(int k = i;k <= j;k++)
{
nsum += p[k];
}
if(nsum > maxsum)
{
maxsum = nsum;
}
}
}
return maxsum;
}//我们发现这个算法的时间复杂度竟然高达O(N^3),这是让人无法忍受的
我们通读代码我们可以发现,这个算法竟然采用了三个for循环,嵌套过度。那么加以优化就有了我们第二种做法。
思路二(进行优化,减少计算)
我们仔细审题,可以发现最大值是和0进行比较,那么当子数列的和是正值的时候才有意义。当计算出来负值的时候,便不用计算了更换起点,即向前提起点。那将起点提到哪呢??这是个大问题。
我们可以通过画图来进行分析:
int Maxsum(int * p,int sz)
{
//进行循环枚举
int nsum = 0;
int maxsum = 0;
for(int i = 0;i < sz;i++)
{
nsum = 0;
for(int j = i;j < sz;j++)
{
nsum += p[j];
if(nsum < 0)//通过一些特征来对算法进行优化
{
i = j;
break;
}
}
if(nsum > maxsum)
{
maxsum = nsum;
}
}
return maxsum;
}
可以看到的是这种算法的代码量相对于上一个解法简洁了不少,所以对代码的优化是十分有必要的。
解法三(分治法)
再考虑这个问题之前我们可以将区间无限缩小,当区间只有一个元素或者是只有两个元素的时候,这个问题就十分简单了,最大子序列要不就在左或者右序列,亦或者是在横跨两个序列的中间序列中。所以字串就可以划分成三种:
可以预见的是我们只要将这三个区间的最大子列和求出来,然后进行比较选择最大的那个即可。
代码如下:
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
void cmp(int *p,int * i,int * j)
{
if(*i > *j && *i > *p )
{
*p = *i;
}
if(*j > *i && *j > *p)
{
*p = *j;
}
}
int s_sum(vector<int> & p,int l,int r)
{
//定义中间最大和变量以及传递变量,就是根节点的变量
int sum = 0;
//定义左右和存放的地方,其实基本原理就是在左右枝进行计算和,然后将左右的最大子列和,和在根节点计算的
//中间子列和进行比较
int lsum ,rsum;//存放左右答案的地方
//存放中间序列左右和用于合并
int s1,s2;
//但是在求解中间序列的最大的时候,还是可以进行分解即,分别求解最大子列和,然后进行相加即可
int lsum1,rsum1;
//如果当左右下标相等的时候则将开始缩小到的这个数是否为0
//如果为负数的话则将sum置为0
if(l == r)
{
if(p[l] <= 0)
{
sum = 0;
}
else{
sum = p[l];
}
}
else
{
//进行区分空间
int mid = l + r >> 1;
//先遍历左子树再来右子树
lsum = s_sum(p,l,mid);
rsum = s_sum(p,mid + 1,r);
//对中间序列的左半边进行求解
//对用到的相应变量进行初始化
s1 = 0;
lsum1 = 0;
for(int i = l;i <= mid;i++)
{
lsum1 += p[i];
if(lsum1 > s1)
{
s1 = lsum1;
}
}
s2 = 0;
rsum1 = 0;
for(int j = mid + 1;j <= r;j++)
{
rsum1 += p[j];
if(rsum1 > s2)
{
s2 = rsum1;
}
}
sum = s1 + s2;
//调用三个数比较接口
cmp(&sum,&lsum,&rsum);
}
return sum;
}
int main()
{
int n = 0;
//输入一共几个数字
cin >> n;
vector<int>arr;
int tem = 0;
for(int i = 0;i < n;i++)
{
cin >> tem;
arr.push_back(tem);
}
//进行求子列的最大和
int maxsum = s_sum(arr,0,n - 1);
cout << maxsum << endl;
return 0;
}
原理模式图:
这个方法是比较难理解的方法,所以要结合画图对这个原理加以深刻的理解。
后序
欲买桂花同载酒,终不似,少年游。