有趣的题目

给定一串数,要求找出满足sum[L,R]=aim的最长区间[L,R]。

暴力:枚举起点和终点,O(n)。

优化:如果[L1,R]和[L2,R]的区间和相同且L1<L2,那么我们可以知道[L1,L2-1]的区间和为0。只要加上这段区间和为0的区间,区间长度就会更长,因此我们会选择[L1,R]。

重新考虑:我们可以固定终点选取起点,利用前缀和sum数组,sum[i]=∑j=1,i  Array[j]。当sum[R]=x,我们只需要从1开始找到第一个sum[i]=x-aim,把[1,i]这段区间挖去,就可以得到以R为最终结束的,长度最长的且满足区间和为aim的区间。

实现思路:c++可以采用hash,将数据组织为<区间和,结尾元素索引>。预处理时需要向hash中添加元素<0,0>,表示在不累加任何项时,区间和为0出的最小索引为0。【数组元素从1开始打入】

代码:

#include<cstdio>
#include<map>
using namespace std;
int sum[505];
int main(){
    int n,aim,dif,maxlen=-1;
    scanf("%d%d",&n,&aim);
    for(int i=1;i<=n;i++){
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];
        printf("%d ",sum[i]);
    }
    printf("\n");
    map<int,int> Pair;
    Pair.insert(pair<int,int>(0,0));
    for(int i=1;i<=n;i++){
        dif=sum[i]-aim;
        if(Pair.find(dif)!=Pair.end()){//存在 
            maxlen=maxlen<i-Pair[dif]?i-Pair[dif]:maxlen;
        }
        if(Pair.find(sum[i])==Pair.end()){
            Pair[sum[i]]=i;
        }
    }
    printf("%d",maxlen);
    return 0;
}

 

空间优化代码:

#include<cstdio>
#include<map>
using namespace std;
int main(){
    int n,aim,dif,maxlen=-1,sum,pre=0;
    map<int,int> Pair;
    Pair.insert(pair<int,int>(0,0));
    scanf("%d%d",&n,&aim);
    for(int i=1;i<=n;i++){
        scanf("%d",&sum);
        sum+=pre;
        pre=sum;
        dif=sum-aim;
        if(Pair.find(dif)!=Pair.end()){
            maxlen=maxlen<i-Pair[dif]?i-Pair[dif]:maxlen;
        }
        if(Pair.find(sum)==Pair.end()){
            Pair[sum]=i;
        }
    }
    printf("%d",maxlen);
    return 0;
}

 

 

变形1:一串数有只有奇数和偶数,求最长的区间满足奇数与偶数个数相等。

思路:把奇数变1偶数变-1,按照上面的解题思路求aim=0的最长区间即可。

 

变形2:一个数组有0,1,2,求最长的区间满足1与2个数相等。

思路:0,1不变,2变-1,aim=0。

 

变形3:一个数串,可以任意划分,要求划分后形成的区间抑或结果为0的区间数最多。

【需要一点dp】:

首先明确几点抑或的性质:1 . x^x=0   2 . 0^1=1。

像子数组划分问题的普遍思路就是固定结尾找符合性质的开头。 

假设当前考虑的数是Ai,我们已经求出了A1~Ai-1的最多符合性质数组的个数,分两种情况:

A . 如果i不是满足划分性质的最后一个区间的最后一个数,那么dp[i]=dp[i-1]。

B . 如果i是满足划分性质的最后一个区间的最后一个数,我们应该找到这个区间的开始j,那么dp[i]=dp[j-1]+1。

综上所述,两者取大即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值