如何找到[L,R]之间大小的最大和子数组
Posted
技术标签:
【中文标题】如何找到[L,R]之间大小的最大和子数组【英文标题】:How to find maximum sum subarray of size between [L, R] 【发布时间】:2020-10-31 20:39:49 【问题描述】:给定一个包含正整数和负整数的数组,如何找到长度在L
和R
之间的最大和子数组(连续子数组)?
例如: 如果数组是
-1 3 -2 5 3 -5 2 2
和L = 1
和R = 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]之间大小的最大和子数组的主要内容,如果未能解决你的问题,请参考以下文章