给定K个整数组成的序列{ N1 , N2 , …, NK },“连续子列”被定义为{ Ni, Ni+1 , …, Nj },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
本题旨在测试各种不同的算法在各种数据情况下的表现。
输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。
输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。
输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20
这题一共有四种解法(在《数据结构与算法分析—c语言版》p21开始)
这里我就写了两种,因为我觉得这两种好一点也许吧
有一说一也是确实好,写完可以去PTA这题交一发
(题目传送门:https://pintia.cn/problem-sets/15/problems/709)
第一种会比第二种更好理解,但第二种代码更简短运行时间也更少(写出来的两种都比书上另外两种要快)
第一种
三个自定义函数,主要看第二个,第一个就是找Max前半段sum,Max后半段sum,前后半段maxsum,这三个数的最大值,第二个里面有一点点递归不难理解的 虽然我看了一会 ,最后一个函数就是让最后用起来更方便,要是不想写,直接第二个进主函数也是可以用的(就直接Maxsubsum(a,0,n-1))
看代码吧
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int a[100000];
int max3(int z,int x,int v) //找最大
//(我这种找最大的写法会warning,大概意思就是警告我这个函数可能会没有返回值,但其实没事,这个函数已经判断了全部情况)
//不想用的也有其他写法(我写在下面了)
{
if(z>=x&&z>=v)
return z;
if(x>=z&&x>=v)
return x;
if(v>=z&&v>=v)
return v;
}
/*
int max3(int z,int x,int v) //这就是第二种找Max的
{
int max=z;
if(x>max)
max=x;
if(v>max)
max=v;
return max;
}
*/
int Maxsubsum(int a[],int left,int right)
{
int maxleftsum,maxrightsum;
int maxleftbordersum,maxrightbordersum;
int leftbordersum,rightbordersum;
int center,i;
center=(left+right)/2; //找中间位置
if(left==right) //相同就代表只有一个元素
if(a[left]>0) //题目只要大于0(a[left]==a[right]所以用哪个都一样)
return a[left];
else
return 0;
maxleftsum=Maxsubsum(a,left,center); //递归(套几个数据跟着一起走大概能明白)
maxrightsum=Maxsubsum(a,center+1,right);
maxleftbordersum=0;
leftbordersum=0;
for(int i=center;i>=left;i--) //找左半部分最大
{
leftbordersum+=a[i];
if(leftbordersum>maxleftbordersum)
maxleftbordersum=leftbordersum;
}
maxrightbordersum=0;
rightbordersum=0;
for(int i=center+1;i<=right;i++) //找右半部分最大
{
rightbordersum+=a[i];
if(rightbordersum>maxrightbordersum)
maxrightbordersum=rightbordersum;
}
return max3(maxleftbordersum,maxrightbordersum,maxleftbordersum+maxrightbordersum); //比较哪个更大返回大的
}
int Maxsubsequencesum(int a[],int n) //简化一下到时候主函数里更好用
{
return Maxsubsum(a,0,n-1);
}
int main()
{
int k,sum=0;
scanf("%d",&k);
for(int i=0;i<k;i++)
scanf("%d",&a[i]);
sum=Maxsubsequencesum(a,k);
printf("%d\n",sum);
return 0;
}
第二种
是一种联机算法 (用的时间少(O(N))) 书上解释是说它只对数据进行一次扫描,一旦完成对a[i]的读入和处理就不需要再记忆它了。
代码主要意思就是如果a[i]是负数就变为0,就舍弃,然后下一个数a[i+1]变为新的字串起始点,大于Max的就保存,循环。
看代码吧
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int a[100000];
int Maxsubsequencesum(int a[],int n)
{
int thissum,maxsum;
thissum=maxsum=0;
for(int i=0;i<n;i++)
{
thissum+=a[i];
if(thissum>maxsum)
maxsum=thissum;
else if(thissum<0)
thissum=0;
}
return maxsum;
}
int main()
{
int k,sum=0;
scanf("%d",&k);
for(int i=0;i<k;i++)
scanf("%d",&a[i]);
sum=Maxsubsequencesum(a,k);
printf("%d\n",sum);
return 0;
}
不理解可以再去看看其他的一些联机算法的例子,有些大佬写的超好的说