C - 顺序表应用7:最大子段和之分治递归法

C - 顺序表应用7:最大子段和之分治递归法

Description

 给定n(1<=n<=50000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。

 

注意:本题目要求用分治递归法求解,除了需要输出最大子段和的值之外,还需要输出求得该结果所需的递归调用总次数。

 

递归调用总次数的获得,可以参考以下求菲波那切数列的代码段中全局变量count的用法:

#include
int count=0;
int main()
{
    int n,m;
    int fib(int n);
    scanf("%d",&n);
    m=fib(n);
    printf("%d %d\n",m,count);
    return 0;
}
int fib(int n)
{
    int s;
    count++;
    if((n==1)||(n==0)) return 1;
    else s=fib(n-1)+fib(n-2);
    return s;
}
 

Input

第一行输入整数n(1<=n<=50000),表示整数序列中的数据元素个数;

第二行依次输入n个整数,对应顺序表中存放的每个数据元素值。

Output

一行输出两个整数,之间以空格间隔输出:

第一个整数为所求的最大子段和;

第二个整数为用分治递归法求解最大子段和时,递归函数被调用的总次数。

Sample

Input 

6
-2 11 -4 13 -5 -2

Output 

20 11

分治法求解最大子段和,思想就是将数组分成左右两部分,分别求对应的子段和,最后还有一种可能,最大子段和处于左右两部分之间,所以我们要进行三次比较才能求出到底是哪一部分才是最大的子段和。其实不需要清楚每一步是怎么做的,我们只需要理解大致的想法,将代码交给计算机,具体细微的运算交给计算机去做。

具体见代码注释。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int a[100000]={0};//在宏观定义一个数组,其实没必要定义这么大的,比五万大一点即可 
int flag=0;//计数变量,计算调用函数的次数 
int sum=0;
int max_function(int l,int r)//分治求最大字段和函数 
{
	flag++;//每次调用函数先加一 
	if(l==r)//左等右,表示递归到最后一个 
	{
		if(a[l]>0)//如果数据大于零,就将sum初始化为此数据,如果小于0则不需要将sum等于
		//因为如果数据小于0,定义子段和为0,最后一个数据也相当与一个长度为1的子段 
			sum=a[l];
		else
			sum=0;
	}
	else//分治函数的主要部分 
	{
		int mid=(l+r)/2;//将数组一分为二进行分治 
		int l_res=max_function(l,mid);//左半部分遍历 
		int r_res=max_function(mid+1,r);//右半部分遍历 
		int s=0;
		for(int i=l;i<=r;i++)//遍历结束后,从最左边开始,到最右边,不断将sum与s进行比较 
		{
			if(s+a[i]>0)//s相当于从最左加到最右,分治法需要有三个结果进行比较,左半部分的sum
			//右半部分的sum,以及一部分处于左边,一部分处于右边的子段构成的子段和s 
				sum=max(sum,s=s+a[i]);
			else
				s=0;
		}
		sum=max(sum,l_res);//sum与s比较完之后与左半部分比较 
		sum=max(sum,r_res);//sum与左半部分比较完后与右半部分比较 
	}
	return sum;//返回最大值 
}
int main()
{
	ios::sync_with_stdio(false);//提高输入输出效率。要加这一句话,不加的话会TLE
	// c++中的cin和cout的效率比print和scanf 要低很多
	//如果不想加这句话,就把输入输出换成print和scanf,如果用cin,cout的话就要加上 
	int n,res;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	res=max_function(0,n-1);
	cout<<res<<" "<<flag<<endl;
//不要像下面那样输出,输出的flag一直为0,因为二者的输出是同时进行的,会导致函数中的flag变化了但是输出的flag依旧为初始值 
//	cout<<max_function(0,n-1)<<" "<<flag<<endl; 
	return 0;
}

本题时间要求比较严格,只有10ms,如果想用cin,cout进行数据的输入输出就必须写入 ios::sync_with_stdio(false);作用提高输入输出效率。对于cin,cout来说,5w的数据量在10ms处理完已经很吃力了。

关于ios::sync_with_stdio(false);可以参考https://blog.csdn.net/weixin_44015865/article/details/84974373

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 最大子段和问题是指在一个数列中找到一个连续的序列,使得该序列的和最大分治递归是一种解决该问题的方。 具体步骤如下: 1. 将原数列分成两个序列,分别求出左序列的最大子段和、右序列的最大子段和和跨越中点的最大子段和。 2. 左序列的最大子段和、右序列的最大子段和和跨越中点的最大子段和中的最大值即为当前序列的最大子段和。 3. 递归地对左序列和右序列进行上述操作,直到序列长度为1。 4. 最终得到的最大子段和即为原数列的最大子段和。 该方的时间复杂度为O(nlogn),比暴力枚举的O(n^2)更优。 ### 回答2: 最大子段和问题是一个经典的计算机算问题,它的目标是在一个包含正、负数的数组中找到一个连续数组,使得该数组的元素之和最大。在进行优化问题时,可以通过分治递归来解决。顺序表应用7:最大子段和分治递归,就是利用递归来解决最大子段和的问题。 最大子段和分治递归的核心思想是将问题分解成小问题进行解决。具体来说,将整个数组一分为二,分别求出左、右数组最大子段和,然后求出跨越中点的最大子段和。最后,取这三个值中的最大值作为整个数组最大子段和。 对于左、右数组,采用同样的方进行分治递归,直到只剩下一个元素,此时的最大子段和就是该元素本身。而对于跨越中点的最大子段和,需要从中点向左、向右分别寻找最大和,再将左右的最大和相加得到横跨中点的最大和。 最后,将左、右、中三个元素的最大值返回,逐层向上求解,得到整个数组最大子段和。这个算的时间复杂度为O(nlogn),空间复杂度为O(logn)。 最大子段和问题是实际问题中非常常见的问题,比如在股票分析、金融风险评估等领域都有它的应用。通过学习分治递归解决最大子段和问题,可以更好地理解递归思想,并且为实际问题提供解决思路。 ### 回答3: 最大子段和问题是计算一个给定序列中最大序列之和的问题。这个问题可以使用分治来解决。 首先,假设我们要求一个序列的最大子段和。我们可以将这个序列分成两个部分:前半部分和后半部分。然后我们可以再对这两个部分分别求出最大子段和。 接下来,我们需要考虑一个跨过中点的最大子段和。这个最大子段和可以通过计算左半部分的最大后缀和和右半部分的最大前缀和来确定。我们可以将这两个值相加,得到跨越中点的最大子段和。 最后,我们选择这三个值中的最大值。这个最大值就是整个序列的最大子段和。 我们可以用分治递归方式来实现这个方。对于每个序列,我们递归地计算左半部分的最大子段和、右半部分的最大子段和和跨越中点的最大子段和。最后,我们选择三个值中的最大值作为整个序列的最大子段和。 虽然这个方看起来比暴力解决方案要更复杂,但它的时间复杂度为O(n log n),比暴力解决方案的O(n^2)要快得多。因此,分治是一种有效的解决最大子段和问题的方

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值