如何找到[L,R]之间大小的最大和子数组

Posted

技术标签:

【中文标题】如何找到[L,R]之间大小的最大和子数组【英文标题】:How to find maximum sum subarray of size between [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) 中运行的预期解决方案。 【参考方案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/

【讨论】:

这正是我想要的。我应该接受你的回答吗?

以上是关于如何找到[L,R]之间大小的最大和子数组的主要内容,如果未能解决你的问题,请参考以下文章

如何解决最大子数组问题的变化,使总和应该在两个边界之间?

如何使父容器的高度设置为固定 px 高度和子内容高度之间的最大值?

有关数组的算法题

最大和子数组python

分而治之以找到二维数组中两个有序元素之间的最大差异

最大平均值子数组