连续最大和

题目链接

连续最大和

题目描述与示例

一个数组有 N 个元素,求连续子数组的最大和。 例如:[-1,2,1],和最大的连续子数组为[2,1],其和为 3

输入描述

输入为两行。 第一行一个整数n(1 <= n <= 100000),表示一共有n个元素 第二行为n个数,即每个元素,每个整数都在32位int范围内。以空格分隔。

输出描述

所有连续子数组中和最大的值。

示例

输入:

3
-1 2 1

输出:

3

解题思路

题目分析

题目还是比较简短,还是有点小挑战性,就是给定一个数组,判断哪个连续子数组的和最大

个人思路

本来我在想没有做出来的题还要不要写自己的思路,但是吧,自己想出来的东西怎么能嫌弃呢是吧,所以决定还是写写自己的思路吧

  1. 定义两个vector数组num1和num2分别存放一段区间的正数和以及负数和
  2. 因为数组是连续的,合并num1和num2后一定是一正一负
  3. 到这里就有点迷糊了,本来想的是正+负的最大即可,但是忽略了正+负后面还有个正,但是又+上正数后说不定后面的负+正>0那么就又可以加进来
  4. 最后还是没有捋顺提交了

代码展示

写在前面:

本次代码没有通过,不具备参考性,纯属为了记录一下

#include<iostream>
#include<vector>
using namespace std;
int Sum(vector<int> A,size_t& begin)
{
    int sum=0;
    if(A[begin]>=0)
    {
        for(;begin<A.size();begin++)
        {
            if(A[begin]>=0)
                sum+=A[begin];
            else
            {
                begin--;
                return sum;
            }
        }
    }
    if(A[begin]<0)
    {
        for(;begin<A.size();begin++)
        {
            if(A[begin]<0)
                sum+=A[begin];
            else
            {
                begin--;
                return sum;
            }
        }
    }
    return sum;
}
int main()
{
    int n;
    vector<int> arr;
    vector<int> sum1;//保存一段区间的正数和
    vector<int> sum2;//保存一段区间的负数和
    //sum1的第一个位置保存第一段整数和
    //后面的sum1和sum2位置一正一负如果和大于0就添加到sum1[0]
    cin>>n;
    arr.resize(n);
    for(size_t i=0;i<arr.size();i++)
    {
        cin>>arr[i];
    }
    for(size_t i=0;i<arr.size();i++)
    {
        if(arr[i]>=0)
        {
            int tmp=Sum(arr,i);
            sum1.push_back(tmp);
        }
        if(arr[i]<0)
        {
            int tmp=Sum(arr,i);
            sum2.push_back(tmp);
        }
    }
    //到这sum1全是正数和,sum2除第一个元素全是负数和
    //要想和最大,第一个数一定从sum1取,且sum1和sum2一一对应
    int max=0;
    for(size_t i=0;i<sum1.size();i++)
    {
        int all=sum1[i];
        for(size_t j=i;j<sum1.size();j++)
        {
            if(sum1[j+1]+sum2[j]>0)
            {
                all+=sum1[j+1]+sum2[j];
            }
            else
            {
                if(all>max)
                    max=all;
                continue;
            }
        }
    }
    cout<<max;
    return 0;
}

题解

看了题解才知道这道题的处理方法大致上分两种:

  1. 暴力求解:双层循环,i从0-n-1,j从i+1-n-1,定义一个sum和max,sum每次+j后与max判断,如果大于max则max=sum。这样下来即可求解。----这个思路还是比较简单的,不过我却没有想到

  2. DP(Dynamic Programming)求解:

    状态方程式: max( dp[ i ] ) = getMax( max( dp[ i -1 ] ) + arr[ i ] ,arr[ i ] )

dp[i]就是以数组下标为i的数做为结尾的最大子序列和,注意是以i为结尾,比如说现在有一个数组{6,-3,-2,7,-15,1,2,2},dp[2]就是以-2为结尾的,那么显然dp[2]的最大值就是1(6,-3,-2),dp[3]要以7结尾那么以7结尾的子序列最大和就是8(6,-3,-2,7)。现在我们开始细细品一下上面这个递推式,求dp[i]的时候是不是有两种可能,要么就是像上面的dp[3]一样,dp[2]求出来是1了,再加上自己array[3]是最大的,那么还有一种可能就是说如果dp[2]我求出来是-100,那如果我也是dp[2]+array[3]的话是-93,这时候dp[2]反而是累赘,最大就是自己(因为前面定义了必须以i为结尾,也就说必须以7结尾)。

因为暴力求解的思路比较简单且实现也比较容易,这里着重给出DP的代码:

#include <iostream>
#include <vector>
using namespace std;

int main(){
    int n;
    cin >> n;
    vector<int> v(n);
    for(int i = 0; i < n; ++i){
        cin >> v[i];
    }
    int sum = v[0];
    int maxsum = v[0];
    for(int i = 1; i < n; ++i){
        sum = sum > 0 ? sum + v[i] : v[i];
        maxsum = max(sum, maxsum);
    }
    cout << maxsum;
    return 0;
}

以上就是本题的常规题解了,下面介绍一个巧解,还是先上代码:

#include <iostream>
using namespace std;
int main(){
	int n,curSum=0,maxSum=-1e5;
	cin>>n;
	int arr[n];
	for(int i=0;i<n;i++){
		cin>>arr[i];
		curSum+=arr[i];
		if(curSum>maxSum){
			maxSum=curSum;
		}
        if(curSum<0){
			curSum=0;
		}
	}
	cout<<maxSum<<endl;
	return 0;
}

原理的话我有点不太好描述,这里画个图讲解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WimDKnxz-1653810776361)(C:\Users\lwz\AppData\Roaming\Typora\typora-user-images\image-20220524162907109.png)]

前面应该都还是挺好理解的,至于为什么当curSum小于0的时候要归零,实际上当curSum小于0的时候,因为后面还要执行+=arr[i],一个求最大值的问题中加上一个负数还不如不加,简单理解就是说如果-7的时候不归零再加上1,还不如直接舍弃-7取1来的大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值