Leetcode OJ: Maximun Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [−2,1,−3,4,−1,2,1,−5,4]
,
the contiguous subarray [4,−1,2,1]
has the largest sum = 6
.
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
求子串最大和,第一次看到这题是在sogou的笔试,当时手写代码,用了动态规划,以为就OK,谁知道在LeetCode上一敲各种bug。。
不过,总的思想是没有错的,只是要注意的点比较多,虽然不是最优的解法,但也陈述一下吧。
LZ先把问题想了想,求子串和最大,那什么是子串?拿以上数组为例,我们把子串的两边划出来。
[−2,1,−3, | 4,−1,2,1, |−5,4]
1 class Solution { 2 public: 3 int maxSubArray(int A[], int n) { 4 if (n == 1) 5 return A[0]; 6 vector<int> fsum(n-1, 0), bsum(n-1, 0); 7 vector<bool> fflag(n-1, false), bflag(n-1, false); 8 int tsum = A[0]; 9 fsum[0] = A[0]; 10 fflag[0] = true; 11 12 for (int i = 1; i < n-1; ++i) { 13 tsum += A[i]; 14 if (tsum < fsum[i-1]) { 15 fsum[i] = tsum; 16 fflag[i] = true; 17 } else { 18 fsum[i] = fsum[i-1]; 19 } 20 } 21 tsum = A[n-1]; 22 bsum[n-2] = A[n-1]; 23 bflag[n-2] = true; 24 for (int i = n-2; i > 0; --i) { 25 tsum += A[i]; 26 if (tsum < bsum[i]) { 27 bsum[i-1] = tsum; 28 bflag[i-1] = true; 29 } else { 30 bsum[i-1] = bsum[i]; 31 } 32 } 33 tsum += A[0]; 34 int ret = tsum; 35 for (int i = 0; i < n-1; ++i) { 36 int tmp = tsum - min(fsum[i], bsum[i]); // 只考虑左侧或右侧 37 if (!fflag[i] || !bflag[i]) { // 分隔线不重合 38 if (tmp < tsum - fsum[i] - bsum[i]) 39 tmp = tsum - fsum[i] - bsum[i]; 40 } else { // 分隔线重合 41 if (i > 0 && tmp < tsum - fsum[i-1] - bsum[i]) { 42 tmp = tsum - fsum[i-1] - bsum[i]; 43 } 44 if (i < n-2 && tmp < tsum - fsum[i] - bsum[i+1]) { 45 tmp = tsum - fsum[i] - bsum[i+1]; 46 } 47 } 48 if (tmp > ret) { 49 ret = tmp; 50 } 51 } 52 return ret; 53 } 54 };
成功拖低了平均水平,然后实在想知道有没更好的方法,上网一搜果断有的,找到了传说中的Kadane算法(自行百度吧),代码只有几行(伤透了我的心。。),这里直接贴一下吧:
1 class Solution { 2 public: 3 int maxSubArray(int A[], int n) { 4 int ans = 0, maxn = INT_MIN; 5 for(int i = 0; i < n; i++){ 6 if(ans < 0) ans = 0; 7 ans += A[i]; 8 maxn = max(maxn, ans); 9 } 10 return maxn; 11 } 12 };
然后LZ蛋疼地去看下运行时间,发现居然比我的方法慢?不能理解。多提交了几次,也是这样,于是想了想,发现这代码里面用了max函数,而且每次都要重要赋值,其实可以先判断再赋值。LZ加了个判断再赋值,然后提交,果断快了,嗯,这是小插曲,大家可以不用管LZ自娱自乐。
然后就是分治的思路了,这个其实我也不太懂,不过看别人的代码确实还是学了不少的,直接转别人的代码吧(以上Kadane算法也是别人的代码)。
三个状态:begin, end, mid=(begin+end)/2
1. 在A[begin..mid-1]间
2. 在A[mid+1..end]间
3. 包含A[mid]的区间
1 class Solution { 2 public: 3 int divide(int A[], int begin, int end) { 4 if (begin == end) return A[begin]; 5 if (begin + 1 == end) 6 return max(max(A[begin], A[end]), A[begin] + A[end]); 7 int mid = begin + (end - begin) /2; 8 int lmax = divide(A, begin, mid - 1); 9 int rmax = divide(A, mid + 1, end); 10 int mmax = A[mid]; 11 int tmp = A[mid]; 12 for (int i = mid-1; i >= begin; --i) { 13 tmp += A[i]; 14 if (tmp > mmax) 15 mmax = tmp; 16 } 17 tmp = mmax; 18 for (int i = mid+1; i <= end; ++i) { 19 tmp += A[i]; 20 if (tmp > mmax) { 21 mmax = tmp; 22 } 23 } 24 return max(max(lmax, rmax), mmax); 25 } 26 int maxSubArray(int A[], int n) { 27 return divide(A, 0, n-1); 28 } 29 };