最大子序列系列问题

  1. 问题描述

给定一个序列,求最大子序列和

这个问题可以用贪心算法解决,依次相加,加正数序列增大,加负数减小,如果此时序列小于0,则说明此前序列应舍去,重新从后面开始加,每次记录现在的值,判断是否最大,大于max则更新max

   对了还要特判全是负数的情况

    贪心代码

#include<bits/stdc++.h>
using namespace std;
int arr[1000000];
bool x=0;
int n,ans=0,mmax=-100000000;//一个非常小的数 
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>arr[i];
		if(arr[i]>0)
		x=1;//特判准备 
	}
	if(x==0)//全为负数特判,直接找最大值
	{
		for(int i=1;i<=n;++i)
		{
			if(arr[i]>mmax)
			{
				mmax=arr[i];
			}
		 } 
		 cout<<mmax;
		 return 0;
	 } 
	for(int i=1;i<=n;++i)
	{
		ans+=arr[i];//累加 
		if(ans<0)//舍去,重新累加 
		{
			ans=0;
		}
		if(ans>mmax)
		{
			mmax=ans;//更新最大值 
		}
	}
	cout<<mmax;
		return 0;
}

同时,这题也可以动态规划

设dp[i]为以结尾的最大子序列和,则dp【i】为max{arr[i],dp[i-1]+arr[i]}依次推出,则dp【i】中的最大值为最大子序列和

dp代码

#include<bits/stdc++.h>
using namespace std;
int dp[1000000];
int n,mmax;
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>dp[i];
	}
	mmax=dp[1];
	for(int i=2;i<=n;++i)
	{
		dp[i]=max(dp[i],dp[i-1]+dp[i]);
		if(dp[i]>mmax)
		{
			mmax=dp[i];
		}
	}
	cout<<mmax;
		return 0;
}

2.

但有时,题目会限制子序列的长度最大为m,这时,我们可以结合前缀和的知识来解决,

设s[i]为arr的前缀和,即arr1+arr2+....+arri 求出所有前缀和时间复杂度为n,则问题转化为求是s[i]-s[k],(i-k<=m) 的最大值,可以考虑单调队列,使每个s[i]进入队列一次,找到最大的子序列

#include<bits/stdc++.h>
using namespace std;
int s[1000000];
deque<int> d;
int n,m,mmax;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
	  scanf("%d",&s[i]);//输入序列 
	}
	for(int i=2;i<=n;++i)
	{
	  s[i]+=s[i-1];//求前缀和 
	}
	d.push_back(1); 
	for(int i=2;i<=n;++i)
	{
		if(d.front()<i-m&&!d.empty())//相差超过m无法做差 
		d.pop_front();
		if(s[i]-s[d.front()]>mmax&&!d.empty()) //更新max 
		{
			mmax=s[i]-s[d.front()];
		}
		while(s[i]<=s[d.back()])
		d.pop_back();
		d.push_back(i);
	}
	cout<<mmax;
		return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值