• 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

  • 相关阅读:
    数据库连接JOIN
    Java面试金典
    Collections.sort详解
    Java复合优先于继承
    js算术运算符与数据类型转换
    js数组类型
    js对象类型
    CSS-API(CSS编程接口),CSSOM(css对象模型)
    从零开始--单片机十字路口交通灯控制实验
    matlab用双重循环实现费诺编码
  • 原文地址:https://www.cnblogs.com/flowerkzj/p/3632513.html
Copyright © 2020-2023  润新知