给定一个数组A[0,1,…,n-1],求A的连续子数组,使得该子数组的和最大。
例如:
数组:1,-2,3,10,-4,7,2,-5
最大字数组:3,10,-4,7,2
此问题有以下四种方法
1、 暴力法
2、 分治法
3、 分析法
4、 动态规划法
直接求解A[I,…j]的值,其中,0<=i<n,i<=j<n,因为i,i+1,…j的最大长度为n,所以时间复杂度O(n3)。
//暴力法 int MaxSubArray(int *a, int n) { int maxSum = a[0]; int curSum; for(int i=0; i<n; ++i) { for(int j=i; j<n; ++j) { curSum = 0; for(int k=i; k<=j; ++k) { curSum+=a[k]; } if(curSum>maxSum) maxSum = curSum; } } return maxSum; }
将数组从中间分开,那么最大子数组要么完全在左半边数组,要么完全在右半边数组,要么跨立在分界点上。
完全在左数组、右数组,递归可以解决
跨立在分界点上:实际上是左数组的最大后缀和右数组的最大前缀的和。因此,从分界点向前扫,向后扫即可。
//分治法
int maxAddSub(int *a, int from, int to) { if(from==to) return a[from]; int mid = (from + to) / 2; int m1 = maxAddSub(a,from,mid); int m2 = maxAddSub(a,mid+1,to); int now = a[mid], left = a[mid]; int i; for(i=mid-1; i>=from; --i) { now+=a[i]; left = max(now,left); } int right = a[mid+1]; now = a[mid+1]; for(i=mid+2; i<=to; ++i) { now+=a[i]; right = max(now,right); } int m3 = left + right; return max(max(m1,m2),m3); }
设前缀和p[i]=a[0]+a[1]+a[2]+...+a[i]
s[i,j]=p[j]-p[i-1],表示从数组第i个位置到第j个位置的元素和
算法过程:
遍历一遍数组,求前缀p[i],0<=i<n,p[i]=p[i-1]+a[i]
再遍历一遍,找到最小的前缀p[i],赋值给min
再遍历一遍各前缀,令p[i]-min最大,即是以a[i]结尾的数组中最大的子数组
因为该过程都是线性的,所以时间复杂度为O(N)
//分析法 int maxAddSub(int *a, int n) { vector<int> p; p.push_back(a[0]); int i; for(i=1; i<n; ++i) p.push_back(a[i]+p[i-1]); int min = 0; for(i=0; i<n; ++i) if(p[i]< min) min = p[i]; int max = 0; for(i=0; i<n; ++i) if(p[i]-min > max) max = p[i]-min; return max; }
//动态规划 int MaxSubArray_dynamic(int *a, int n) { int result = a[0]; int sum = a[0]; for(int i=0; i<n; ++i) { if(sum>0) sum+=a[i]; else sum = a[i]; if(sum > result) result = sum; } return result; }
// Maximum continuous word groups.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> #include <vector> using namespace std; //暴力法 int MaxSubArray(int *a, int n) { int maxSum = a[0]; int curSum; for(int i=0; i<n; ++i) { for(int j=i; j<n; ++j) { curSum = 0; for(int k=i; k<=j; ++k) { curSum+=a[k]; } if(curSum>maxSum) maxSum = curSum; } } return maxSum; } int max(int a, int b) { return a>b ? a : b; } //分治法 int maxAddSub(int *a, int from, int to) { if(from==to) return a[from]; int mid = (from + to) / 2; int m1 = maxAddSub(a,from,mid); int m2 = maxAddSub(a,mid+1,to); int now = a[mid], left = a[mid]; int i; for(i=mid-1; i>=from; --i) { now+=a[i]; left = max(now,left); } int right = a[mid+1]; now = a[mid+1]; for(i=mid+2; i<=to; ++i) { now+=a[i]; right = max(now,right); } int m3 = left + right; return max(max(m1,m2),m3); } //分析法 int maxAddSub(int *a, int n) { vector<int> p; p.push_back(a[0]); int i; for(i=1; i<n; ++i) p.push_back(a[i]+p[i-1]); int min = 0; for(i=0; i<n; ++i) if(p[i]< min) min = p[i]; int max = 0; for(i=0; i<n; ++i) if(p[i]-min > max) max = p[i]-min; return max; } //动态规划 int MaxSubArray_dynamic(int *a, int n) { int result = a[0]; int sum = a[0]; for(int i=0; i<n; ++i) { if(sum>0) sum+=a[i]; else sum = a[i]; if(sum > result) result = sum; } return result; } int _tmain(int argc, _TCHAR* argv[]) { int a[] = {1,-2,3,10,-4,7,2,-5}; int size = sizeof(a)/sizeof(a[0]); double duration; cout<<MaxSubArray(a,size)<<endl; cout<<maxAddSub(a,0,size)<<endl; cout<<maxAddSub(a,size)<<endl; cout<<MaxSubArray_dynamic(a,size)<<endl; return 0; }
原文地址:http://blog.csdn.net/dream_whui/article/details/44944949