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.

More practice:

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]

 红竖杠间是我们要最大和的子串,那么就是要左右两侧的子串和最小,那么就是两侧的和都分别最小则总和最小,即中间的子串即为最大。
以上说的是中心思想,但这里还有好多的情况要讨论。
当红线重合时,那么就是没有子串,则需要其中一条红线左移,或者右移一格。
当只有一条红线时,即只考虑左侧子串,或右侧子串时,也要独立考虑。
提交了N次才通过的代码:
 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 };

代码转自http://blog.csdn.net/xshengh/article/details/12708291

posted @ 2014-03-29 14:45  flowerkzj  阅读(188)  评论(0编辑  收藏  举报