【问题标题】:How to find maximum sum subarray of size between [L, R]如何找到[L,R]之间大小的最大和子数组
【发布时间】:2020-10-31 20:39:49
【问题描述】:

给定一个包含正整数和负整数的数组,如何找到长度在LR 之间的最大和子数组(连续子数组)?

例如: 如果数组是

-1 3 -2 5 3 -5 2 2

L = 1R = 2,答案将是8

我的方法:

我不太确定如何解决这个问题。我想也许它是滑动窗口+ Kadane 的组合。听说前缀和+滑动窗口可能是一个可能的解决方案,但我不知道如何实现。

【问题讨论】:

  • 预处理前缀数组需要 O(n),但它可以告诉您 O(1) 中任何连续子数组的总和,因此您可以线性检查大小为 L、L+1 的片段。 ...,R。但我想应该有比这个 O(n²) 更快的解决方案。
  • R 的最大值是多少?似乎可以轻松推导出 O(N R) 算法,但如果 R​​ 太大则不适用
  • @Damien 即使对于大 R,这也不会比简单的 O(n²) 算法差。
  • R 的最大可能值为 N,N 达到 2*(10)^5。 smyatkin 提出的解决方案是我认为在 n*log(n) 中运行的预期解决方案。

标签: arrays algorithm sub-array


【解决方案1】:

我使用 Cormen 中解释的分而治之的方法来解决这个问题。将数组分成两半。对于一个小数组来说,大小为 2 的最大子数组将是左半部分或右半部分,或者包含左半部分和右半部分的两个元素的交叉点。
例如,如果 arr[]={-3,5} 右半部分是最大子数组。如果 arr[]={3,6} 包含左半边和右半边的两个元素的交叉具有最大子数组。 所以通过使用分而治之的范式来解决这个问题,我们得到了最大的子数组。 它的工作方式是这样的 image posted here c++实现

#include <bits/stdc++.h> 
typedef long long int ll;
using namespace std;
tuple<int ,int, int> Find_Crossing(int arr[],int low,int high);

tuple<int ,int, int> Maximum(int arr[],int low,int high)
{
    cout<<low<<" "<<high<<"\n";
    
    if(low==high)
        return make_tuple(low,high,arr[low]);
    else
    {
    int mid=(low+high)/2;
    int left_low,left_high,left_sum;
    tuple <int ,int ,int > left_ans;
    
    left_ans=Maximum( arr,low,mid);
    tuple <int ,int ,int >right_ans;
    right_ans=Maximum( arr,mid+1,high);
    tuple <int ,int ,int >cross_ans;
    
    cross_ans=Find_Crossing( arr,low,high);
    
    left_sum=get<2>(left_ans);
    
    int right_sum=get<2>(right_ans);
    int cross_sum=get<2>(cross_ans);
    if(left_sum>=right_sum&&left_sum>=cross_sum)
    {
        return left_ans;
    }
    if(right_sum>=cross_sum&&right_sum>=left_sum)
    {
        return right_ans;
    }

    return cross_ans;
    }
}

tuple<int ,int, int> Find_Crossing(int arr[],int low,int high)
{
    cout<<low<<" "<<high<<"\n";
    if(low==high)
        return  make_tuple(low,high,arr[low]);
    int mid=(low+high)/2;
    int l_max=INT_MIN;
    int sum=0;
    int l_index=mid;
    for (int i = mid; i >=low ; --i)
    {
        sum+=arr[i];
        if(sum>l_max)
            {
                l_max=sum;
                l_index=i;
            }   
    }

    int r_max=INT_MIN;
     sum=0;
    int r_index=mid+1;
    for (int i = mid+1; i <=high; ++i)
    {
        sum+=arr[i];
        if(sum>r_max)
        {
            r_max=sum;
            r_index=i;
        }
    }
    //cout<<l_index<<" ";
    
    return make_tuple(l_index,r_index,r_max+l_max);
}
int main()
{   

    int arr[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
    tuple<int,int,int> trying;
    trying= Maximum(arr,0,sizeof(arr)/sizeof(arr[0])-1);
    cout<<get<2> (trying);
}



【讨论】:

    【解决方案2】:
     #include<bits/stdc++.h>
    using namespace std;
    #define maxn 10000
    int a[maxn];
    struct nod{
    int sum,preffixsum,suffixsum,maxsum;
    };
    nod tree[4*maxn];
    

    然后尝试构建段树:

    inline void build(int l,int r,int node)
    {
    if(l==r)
    {
        tree[node].sum=a[l];
        tree[node].preffixsum=a[l];
        tree[node].suffixsum=a[l];
        tree[node].maxsum=a[l];
        return;
    }
    int mid=(l+r)/2;
    int left=2*node;
    int right=2*node+1;
    build(l,mid,left);
    build(mid+1,r,right);
    tree[node].sum=tree[left].sum+tree[right].sum;
    
    tree[node].preffixsum=max(tree[left].preffixsum,tree[left].sum
    +tree[right].preffixsum);
        
    tree[node].suffixsum=max(tree[right].suffixsum,tree[right].sum+tree[left].suffixsum);
        
    tree[node].maxsum=max(tree[node].preffixsum,
    max(tree[node].suffixsum,max(tree[left].maxsum,
    max(tree[right].maxsum,tree[left].suffixsum+tree[right].preffixsum  ))));
    }
    

    然后应用查询:

    nod query( int index, int low, 
    int high, int l, int r{
    
    nod result;
    result.sum = result.preffixsum = 
                 result.suffixsum = 
                 result.maxsum = INT_MIN;
    
    
    if (r < low || high < l)
        return result;
    
    
    if (l <= low && high <= r)
        return tree[index];
    
    int mid = (low + high) / 2;
    
    if (l > mid)
        return query( 2 * index + 1, 
                     mid + 1, high, l, r);
          
    
    if (r <= mid)
        return query( 2 * index , 
                     low, mid, l, r);
    
    nod left = query(2 * index , 
                      low, mid, l, r);
    nod right = query( 2 * index + 1, 
                        mid + 1, high, l, r);
    
    
    result.sum = left.sum + right.sum;
    result.preffixsum = max(left.preffixsum, left.sum + 
                           right.preffixsum);
                             
    result.suffixsum = max(right.suffixsum,
                       right.sum + left.suffixsum);
    result.maxsum = max(result.preffixsum,
                    max(result.suffixsum,
                    max(left.maxsum,
                    max(right.maxsum,
                    left.suffixsum + right.preffixsum))));
                      
    return result;
    }
    

    主要部分:

    int main()
    {
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    build(1,n,1);
    
     cout<< query(1,1,n,2,8).maxsum<<endl;
    
        
     cout<< query(1,1,n,1,2).maxsum<<endl;
    
    }
    

    【讨论】:

      【解决方案3】:

      计算前缀总和,然后迭代索引(索引 >= L && 索引

      【讨论】:

        【解决方案4】:

        如果我正确理解您的问题,有一个 n*logn 解决方案,它确实使用前缀和和滑动窗口。这里解释一下:https://www.geeksforgeeks.org/maximum-sum-subarray-of-size-range-l-r/

        【讨论】:

        • 这正是我想要的。我应该接受你的回答吗?
        猜你喜欢
        • 2014-11-16
        • 1970-01-01
        • 2020-04-29
        • 2011-05-28
        • 2012-10-22
        • 2021-11-27
        • 2011-10-31
        • 2014-04-20
        • 2019-08-31
        相关资源
        最近更新 更多