分治策略

在分治策略中递归地求解一个问题,在每层递归中应用如下三个步骤:

分解 : 将问题划分成一些子问题,子问题的形式与原问题一样,只是规模更小

解决 : 递归地求解子问题。如果子问题的规模足够小,则停止递归直接求解

合并 : 将子问题的解组合成原问题的解。

 

最大子数组问题

分治策略求解(nlg(n) )

low         mid         high

!                !              !

#  #  #  #  #  #  #  #  #

A[low,high]的任何连续子数组的位置必然是下面的三种情况之一:

* 完全位于子数组 A[low,mid] 中,因此low<=i<=j<=mid;

* 完全位于子数组 A[mid+1,high]中, 因此mid<i<=j<=high;

* 跨越了中点,因此low<=i<=mid<j<=high;

所以A[low,high]的一个最大子数组所处的位置必然是三者之一,是 完全位于子数组 A[low,mid] 中,完全位于子数组 A[mid+1,high]中, 跨越了中点的所有子数组中和最大的;

我们可以递归地求解A[low.....mid]和A[mid+1....high]的最大子数组,因为这两个子问题仍是最大子数组问题,只是规模更小,因此剩下的工作就是寻找跨越中点的最大子数组,然后4

在三种情况中选取和最大的;

分治策略代码:

 1 #include<iostream>
 2 using namespace std;
 3 const int inf=999999;
 4 struct answer{
 5     int sum,left,right;
 6 };
 7 answer find_max_crossing_subarray(int *a,int l,int mid,int r){
 8     int leftsum=-inf;
 9     int sum=0;
10     int maxleft;
11     for(int i=mid;i>=l;i--){
12         sum+=a[i];
13         if(sum>leftsum){
14             leftsum=sum;
15             maxleft=i;
16         }
17     }
18     int rightsum=-inf;
19     sum=0;
20     int maxright;
21     for(int i=mid+1;i<=r;i++){
22         sum+=a[i];
23         if(sum>rightsum){
24             rightsum=sum;
25             maxright=i;
26         }
27     }
28     answer ans{leftsum+rightsum,maxleft,maxright};
29     return ans;    
30 }
31 answer find_max_subarray(int *a,int l,int r){
32     if(l==r){
33        answer ans{a[l],l,r};
34        return ans;
35     }else{
36         int mid=(l+r)/2;
37         answer ans1=find_max_subarray(a,l,mid);
38         answer ans2=find_max_subarray(a,mid+1,r);
39         answer ans3=find_max_crossing_subarray(a,l,mid,r);
40         if(ans1.sum>=ans2.sum&&ans1.sum>=ans2.sum){
41             return ans1;
42         }else if(ans2.sum>=ans1.sum&&ans2.sum>=ans3.sum){
43             return ans2;
44         }else{
45             return ans3;
46         }
47     }
48 }
49 int main(){
50     int n;
51     cin>>n; 
52     int a[n];
53     for(int i=0;i<n;i++)
54         cin>>a[i];
55     answer ans=find_max_subarray(a,0,n-1);
56     cout<<ans.left<<" "<<ans.right<<" "<<ans.sum<<endl;
57     return 0;
58 } 

 当然也有线性时间的解法(O(n) )

从头遍历一遍,当sum>max时,更新指针和max; 当sum<0时,将sum归0,因为负数加上去只会让后面的数变小,故舍弃这一段,所以sum=0,更新指针t.

 1 #include<iostream>
 2 using namespace std;
 3 int main(){
 4     int n;
 5     cin>>n;
 6     int a[n];
 7     for(int i=0;i<n;i++)
 8         cin>>a[i];
 9     int sum=0,max=-999999,l=0,r=0,t=0;
10     for(int i=0;i<n;i++){
11         sum+=a[i];
12         if(sum>max){
13             max=sum;
14             l=t; r=i;
15         }else if(sum<0){
16             sum=0;
17             t=i+1;
18         }
19     }
20     cout<<l<<" "<<r<<" "<<max<<endl; 
21     return 0;
22 }

 

posted @ 2018-01-18 18:21  A-Little-Nut  阅读(1178)  评论(0编辑  收藏  举报