左神课堂 数组三连 进阶八视频

题目一

题意:

给你一串数字,然后给你一个数aim,让你求累加和为aim的长度最长的子串的长度。我们规定数字可正,可负,可零

假如给你一串数字:1,0,0,0,0,7,-1,1,10,5

aim等于7,那么最长的子串的长度是7,这个子串是:0,0,0,0,7,-1,1。累加和为7

思路:

我们枚举所有位置,假设我们枚举到了i位置,我们求以i位置为结束位置的满足条件的子串,然后定义一个len变量,维护最长的子串的长度。

我们定义一个map,key代表累加和,value代表最早出现这个累加和的位置。定义一个sum代表累加和,我们假设现在累加到了200位置,累加和为100,aim为40,如果我们要找满足条件的子串,只需要找到i位置前面的位置最早出现累加和为60的位置就可以,然后用i位置减去这个累加和为60的最早的位置,就是累加和为40的满足条件的子串。

代码:

​
#include<algorithm>
#include<iostream>
#include<limits.h>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<map>
#include<set>
#define mod 1000000007
#define MAXN 1000001
typedef long long ll;
using namespace std;
int n,aim;
int root[MAXN];
int GetMaxLen()
{
    int sum=0;
    int len=0;
    map<int,int>q;
    q[0]=-1;//必须先往map中加入这条记录
    for(int i=0;i<n;i++)
    {
        sum+=root[i];
        if(q.find(sum-aim)!=q.end())//如果map中有sum-aim,说明i位置前面的某个位置累加和为sum-aim
            len=max(len,i-q[sum-aim]);//更新len,q[sun-aim]代表i位置前面的位置最早出现累加和为sum-aim的位置
        if(q.find(sum)==q.end())//如果sum这个和在map中没有出现,往map中加入这个值,如果map中存在了sum,就不需要加了
            q[sum]=i;
    }
    return len;//最后返回这个len就好了
}
int main()
{
    cin>>n>>aim;
    for(int i=0;i<n;i++)
        cin>>root[i];
    cout<<GetMaxLen()<<endl;
}

​

上面题目的变形题目:

给你一串数字,让你求满足条件的最长的子串的长度,这个条件是字串奇数和偶数的个数相同。

思路:

我们将数字串中所有奇数变为-1,偶数变为1,然后求累加和为0的最长子串的长度,和上面的题目一模一样。

题目二:

题意:

给定一个数组arr,全是正数 ;一个整数aim,求累加和等于aim的,最长子数组,要求额外空间复杂度O(1),时间复杂度O(n)

思路:

我们运用窗口来做,直接举例说明,假设给定的数组arr为[1,2,5,3,4],aim等于8。

sum代表子数组的累加和,L,R代表窗口的左端点和右端点,len代表累加和等于aim的最长子数组的长度,初始时sum等于0,L,R都等于-1,我们遍历数组中的所有数,在遍历的过程中一共存在三种情况:

情况一:如果当前的sum小于aim,就让R往右移动一个单位,然后sum累加上arr[R]。

情况二:如果当前的sum大于aim,就让L往右移动一个单位,然后sum减去arr[L]。

情况三:如果当前的sum等于aim,我们就更新len,然后sum减去arr[L],然后L往右移动一个单位。

代码:

#include<algorithm>
#include<iostream>
#include<limits.h>
#include <sstream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#define mod 1000000007
#define MAXN 10001
typedef long long ll;
using namespace std;
int n,aim;
int arr[MAXN];
int GetMaxLen()
{
    int len=0;
    int l=-1,r=-1;
    int sum=0;
    for(int i=0;i<n;i++)
    {
        if(sum==aim)//情况三
        {
            len=max(len,r-l);
            sum-=arr[l];
            l++;
        }
        else if(sum<aim)//情况一
        {
            r++;
            sum+=arr[r];
        }
        else//情况二
        {
            sum-=arr[l];
            l++;
        }
    }
    return len;
}
int main()
{
    cin>>n>>aim;
    for(int i=0;i<n;i++)
        cin>>arr[i];
    cout<<GetMaxLen()<<endl;
}

题目三:

题意:

给定一个数组arr,值可正,可负,可0;一个整数aim,求累加和小于等于aim的,最长子数组,要求时间复杂度O(n)

思路:

我们定义两个数组,

MaxnSum[ ]:MaxnSum[i]代表以i位置为开头的子数组的最小累加和为多少。

MaxnSumIndex[ ]:MaxnSumIndex[i]代表得到这个最小累加和到达的arr数组的位置。

如何求这两个数组,我们运用dp的思想,从数组的最后一位往前推,一共分为两种情况:

假设我们求MaxnSum[i]和MaxnSumIndex[i]

情况一:MaxnSum[i+1]位置上的数字>0:   MaxnSum[i]=arr[i]     

 MaxnSumIndex[i]=i

情况二:MaxnSum[i+1]位置上的数字<=0: MaxnSum[i]=arr[i]+MaxnSum[i+1]           

MaxnSumIndex[i]=MaxnSumIndex[i+1]

实现代码:

void GetMax()
{
    for(int i=n-1;i>=0;i--)//从数组最后一个位置往前推
    {
        if(i==n-1||MaxnSum[i+1]>0)//情况一
        {
            MaxnSum[i]=arr[i];
            MaxnSumIndex[i]=i;
        }
        else if(MaxnSum[i+1]<=0)//情况二
        {
            MaxnSum[i]=arr[i]+MaxnSum[i+1];
            MaxnSumIndex[i]=MaxnSumIndex[i+1];
        }
    }
}

下面就是这个题目最精华的部分了:

我们首先定义L   R   sum变量。

sum:累加和.

L:累加和为sum的子数组的头位置.

R:累加和为sum的子数组的末位置.

第一步:首先求出满足累加和小于aim的,以arr数组0位置为开头的子数组,求出这个子数组的终止位置R和这个子数组的累加和sum(通过MaxnSum数组和MaxnSumIndex数组求)。

第二步:然后减去arr[L]位置,更新L(此时的sum=sum-arr[L])。

第三步:执行完第二步后,会分为两种情况:

情况一:sum+MaxnSum[R+1]小于aim,就更新sum和R,然后执行第二步。

情况二:sum+MaxnSum[R+1]大于aim,不更新,直接执行第二步。

最后,需要注意的是,我们要定义一个len变量来维护满足条件的子数组的长度,这个思路有很多边界需要自己coding,自己最好写一下。

代码:

#include<algorithm>
#include<iostream>
#include<limits.h>
#include <sstream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#define mod 1000000007
#define MAXN 10001
typedef long long ll;
using namespace std;
int n,aim;
int arr[MAXN];
int MaxnSum[MAXN];
int MaxnSumIndex[MAXN];
void GetMax()
{
    for(int i=n-1;i>=0;i--)//从数组最后一个位置往前推
    {
        if(i==n-1||MaxnSum[i+1]>0)//情况一
        {
            MaxnSum[i]=arr[i];
            MaxnSumIndex[i]=i;
        }
        else if(MaxnSum[i+1]<=0)//情况二
        {
            MaxnSum[i]=arr[i]+MaxnSum[i+1];
            MaxnSumIndex[i]=MaxnSumIndex[i+1];
        }
    }
}
int GetMaxLen()//核心部分
{
    int L=-1,R=0,len=0;//这就是我们定义的变量
    int sum=0;
    while (R<n)//这个while循环是第一步
    {
        sum+=MaxnSum[R];
        if(sum>aim)
        {
            sum-=MaxnSum[R];
            break ;
        }
        R=MaxnSumIndex[R]+1;
    }
    len=max(len,R-L-1);//始终维护len变量
    while (R<n)//第二步和第三步
    {
        L++;
        sum-=arr[L];//减去子数组的头位置
        while (R<n)//继续尝试sum加上MaxnSum[R]是否小于aim
        {
            sum+=MaxnSum[R];
            if(sum>aim)//情况二
            {
                sum-=MaxnSum[R];
                break ;
            }
            //如果程序能够运行到这里,说明是情况一
            R=MaxnSumIndex[R]+1;//这个地方是一个细节操作
        }
        len=max(len,R-L-1);//维护len变量
    }
    return len;//返回最长的子数组的长度
}
int main()
{
    cin>>n>>aim;
    for(int i=0;i<n;i++)
        cin>>arr[i];
    GetMax();
    cout<<GetMaxLen()<<endl;
}

这个题目很难,思路上很难,代码coding上也不简单,最好自己亲自做一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值