中国大学MOOC-陈越、何钦铭-数据结构

01-复杂度1 最大子列和问题


题目
给定K个整数组成的序列{ N 1 , N 2 , . . . , N k N_1,N_2,...,N_k N1,N2,...,Nk},“连续子列”被定义为{ N i , N i + 1 , . . . , N j N_i,N_i+1,...,N_j Ni,Ni+1,...,Nj },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:

  • 数据1:与样例等价,测试基本正确性;
  • 数据2:10^2个随机整数;
  • 数据3:103个随机整数;
  • 数据4:104个随机整数;
  • 数据5:105个随机整数;

输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。

输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:

6
-2 11 -4 13 -5 -2

输出样例:

20

第三种算法 —— 分而治之
这个算法主要是递归,(呜呜呜,我就不大会写递归),主要思想大概是这样,先将这个数组分成两组,左边一组,右边一组,假设我找到左边这组的最大子列和leftmax,右边这组的最大子列和rightmax,那是不是最大值就在leftmax和rightmax中得到呢?不是的。
存在这样一种情况,左边这组的某几个数和右边的某几个数组合比leftmax和rightmax都大,即跨越分界线。
1.还有一个问题,我一开始没看明白,

for(int i=m;i>=left;i--)
	{	
		sum2+=a[i];
		maxleft=max(sum2,maxleft);
	}

这段代码是如何获得分界线左侧最大子项和的?
以8,-2,11,-4 ||为例
从分界线往左加,
①sum=-4,current=0(初始化为0),经比较current=0
②sum=11-4=7,current=0,经比较current=7
③sum=-2+11-4=5,current=7,经比较current=7
④sum=8-2+11-4=13,current=7,经比较current=13
也就是说sum一直在求各项和,current一直保存的是当前状态的最大子项和
2.有没有想过这样一种情况,-2,-1,11,-4,最大连续子项是{-1,11},看起来它并不是从左往右加,也不是从分界线开始加,好像是从中间某个位置取的,那这种情况是不是被我们之前的算法忽略了呢?
其实不是的,还有递归呢!!!这时递归的作用就体现出来了,{-2,-1 || 11,-4},这时是不是就是跨越分界线(左边这组的某几个数和右边的某几个数组合比leftmax和rightmax都大)这种情况呢
在这里插入图片描述

#include<stdio.h>
#define MAXSIZE 100000
int max(int x1,int x2)
{
	return x1>x2?x1:x2;
}
int findmax(int a[],int left,int right)
{
	int sum1=0,sum2=0,sum3=0,m,max1,maxleft,maxright,now;
	int leftmax,rightmax;
	if( left == right )  
	{ /* 递归的终止条件,子列只有1个数字 */
            if( a[left] > 0 )  
				return a[left];
            else 
				return 0;
    }

	m=(left+right)/2;
	leftmax=findmax(a,left,m);
	rightmax=findmax(a,m+1,right);
	max1=max(leftmax,rightmax);
	for(int i=m;i>=left;i--)
	{	
		sum2+=a[i];
		maxleft=max(sum2,maxleft);
	}
	for(int i=m+1;i<=right;i++)
	{	
		sum3+=a[i];
		maxright=max(sum3,maxright);

	}
	now=max(max1,maxleft+maxright);
	return now;

}
void main()
{
	int K,i,a[MAXSIZE],sum=0;
	scanf("%d",&K);
	for(i=0;i<K;i++)
	{
		scanf("%d",&a[i]);
	}
	sum=findmax(a,0,K);
	printf("%d",sum);
}

第四种算法——在线学习
这个算法,陈越姥姥讲的很详细了,sum一直在求各项和,current一直保存的是当前状态的最大子项和,重点理解一下

else if(sum<0)
	sum=0;

这段代码为我们抛弃负子列,保证最大子列和递增,这还是感觉不是很明白,先留个坑

#include<stdio.h>
#define MAXSIZE 100000
int findmax(int a[],int k)
{
	int current=0,sum=0;
	for(int i=0;i<k;i++)
	{
		sum+=a[i];
		if(current<sum)
			current=sum;
		else if(sum<0)
			sum=0;

	}
	return current;
}
void main()
{
	int K,i,a[MAXSIZE],sum=0;
	scanf("%d",&K);
	for(i=0;i<K;i++)
	{
		scanf("%d",&a[i]);
	}
	sum=findmax(a,K);
	printf("%d",sum);
}

分而治之在自己的编译器上没问题,在pta上总是过不了,显示答案错误,搬了几个博客的代码上去也是显示答案错误,如果有可以通过的代码,记得告诉我啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值