C1-E-最大子数组问题

题目描述

假定你获得了投资挥发性化学公司的机会。与其他公司一样,该公司的股票价格是不稳定的。你被准许可以在某个时刻买进一股该公司的股票,并在之后的某个时期将其卖出。你可以了解股票将来的价格,使得自己的利益最大化。

你的任务是根据每天的股票价格,求得最大的收益。

输入
一个整数n,表示天数

接下来一行,n个整数,用空格隔开,表示每天的股价。

输出
一个整数,表示最大收益。

输入样例

7
3 2 3 4 1 10 2

输出样例

9

样例解释
第5天以1买入,第6天以10卖出,收益最大,为10-1=9
数据范围
n < 1e6

思路分析

把每一天的股票价格减去前一天的价格得到股票价格的起落数组,求这个数组的最大子数组,就得到了最大收益。
经典的最大子数组问题。
由于此处我们想练习分治,我们先讨论用分治法求解最大子数组问题的算法。
时间复杂度O(nlogn)。

最大子数组的出现位置只有三种可能:
1.在左子数组中
2.在右子数组中
3.跨越两个子数组(即跨越中间点)
使用函数FindChileArray来分离左右数组,并得到左右子树组分别的最大值left_sum和right_sum,然后使用FindChileArray_center来求跨越中间点的子数组的最大值sum,最后收益取left_sum、right_sum和sum中最大的一个。
FileChileArray_center求跨越中间点的最大子数组的方法:
从中间点开始,分别向左向右扩展,并在最大值处停下。

AC代码

#include<stdio.h>
int data[1000010];
int data2[1000010];
int FindChileArray(int arr[],int low,int high);
int FindChileArray_center(int arr[],int low,int high,int mid);
int main()
{
	int i,n;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%d",&data[i]);
		if(i!=0) data2[i-1]=data[i]-data[i-1];
	}
	printf("%d",FindChileArray(data2,0,n-1));
}
int FindChileArray(int arr[],int low,int high)
{
	int left_sum=-500000,right_sum=-500000;
	int sum=0,mid,max_left,max_right;
	mid=(low+high)/2;
	if(low==high)  return arr[low];
	else
	{
		left_sum=FindChileArray(arr,low,mid);
		right_sum=FindChileArray(arr,mid+1,high);
		sum=FindChileArray_center(arr,low,high,mid);
		
		if(left_sum>right_sum&&left_sum>sum)  return left_sum;
		else if(right_sum>left_sum&&right_sum>sum)  return right_sum;
		else return sum;
	}
}
int FindChileArray_center(int arr[],int low,int high,int mid)
{
	int left_sum=-500000,right_sum=-500000;
	int sum=0;
	int i;
	int max_left,max_right;
	for(i=mid;i>low;i--)
	{
		sum+=arr[i];
		if(sum>left_sum)
		{
			left_sum=sum;
			max_left=i;
		}
	}
	sum=0;
	for(i=mid+1;i<high;i++)
	{
		sum+=arr[i];
		if(sum>right_sum)
		{
			right_sum=sum;
			max_right=i;
		}
	}
	return left_sum+right_sum;
}

拓展——动态规划法

可以在线性时间内求出最大子数组问题的答案。
建立一个数组dp[],其中dp[i]表示以arr[i]为结尾的最大子数组的和值。那么dp[i+1]要么是之前的最大子数组dp[i]加上本身,要么就是本身。
数学归纳法:
dp[0]=arr[0]
dp[1]=max(dp[0]+dp[1],dp[1])
……
dp[i+1]=max(dp[i]+arr[i+1],dp[i+1])
遍历一遍即可在线性时间内得到最大收益

#include<stdio.h>
int data[1000010];
int data2[1000010];
int dp[1000010];
int max(int a,int b);
int maxSubArray(int nums[],int n);
int main()
{
	int i,n;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%d",&data[i]);
		if(i!=0) data2[i-1]=data[i]-data[i-1];
	}
	printf("%d",maxSubArray(data2,n));
}
int max(int a,int b)
{
	return a>b?a:b;
}
int maxSubArray(int nums[],int n)
{
	int i;
	if(n==0) return 0;
	dp[0]=nums[0];
	int max_sub_num=dp[0];
	for(i=1;i<n;i++)
	{
		dp[i]=max(dp[i-1]+nums[i],nums[i]);
		if(dp[i]>max_sub_num)  max_sub_num=dp[i];
	}
	return max_sub_num;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值