问题描述:

      连续子序列最大和,其实就是求一个序列中连续的子序列中元素和最大的那个。

比如例如给定序列:

{ -2, 11, -4, 13, -5, -2 }

其最大连续子序列为{ 11, -4, 13 },最大和为20。

===============================================================

问题分析

1.首先最朴素的方法是暴力 O(n^3)

直接两个for循环枚举子序列的首尾,然后再来个循环计算序列的和,每次更新和的最大值。

但是这种方法的复杂度是O(n^3),效率实在太低了。。。

————————————————————————————————————————————————

2.第二种方法是预处理 O(n^2)

在读入的时候将前面数的和放在数组中,就能得到一个数组sum[i]储存前i个数的和。然后两重循环枚举首尾,利用sum数组迅速求出子序列的和。

其实这种方法只是优化了前面那种方法的计算和的循环,复杂的是O(n^2),也很糟糕。
————————————————————————————————————————————————

3.第三种是利用分治思想 O(nlogn)

分治算法但看代码不是很好理解,其实思想很简单,就是把序列分成两块计算,用递归分别求出两块序列中的最大子序列和,然后从序列中间向两边遍历求出包含中心的序列的最大和。返回最大的那个序列和。

递归真的很神奇,一直把问题分解乘小问题交给下一层递归处理,直到最底层。

用分治算法的复杂度好了一些,是O(nlogn),虽然不是最优解,但是理解这种算法的确能让我们对递归理解得更加深刻。
————————————————————————————————————————————————

4.第四种是累积遍历算法 O(n)

遍历序列的时候对Sum进行累计,如果Sum累积后小于0的话就把Sum重置为负无穷,每次更新Sum的最大值。最后便能求出最大值。

其实这个算法就是把序列分为好几块,每一块满足:对于任意k,前k个数的和不会小于0(小于0就会分裂成两块了),当前i个数的和大于最大值时就进行更新,而最大值的左边界就是该块序列的第一个,右边界是第i个。

时间复杂度为O(n),而且可以一边读取一边处理,不需要开辟数组来存,空间也很省。

------------------------------------------------------------------------------

举个例子:

-10   1  2  3  4  -5   -23   3  7    -21   (num)

-10 | 1  3  6 10  8 | -23 |  3 10 | -21   (Sum)(|号为该处Sum被清零)

由于10是Sum的最大值,所以,红色的那块就是要求的范围了。

------------------------------------------------------------------------------

    但是为什么所求序列为什么是在序列块中并且是以序列块第一个数作为开头呢?

:如果不是这样的话,会有几种情况:

1  该目标序列跨越了几个块。由于Sum在<=0的时候会重置为负无穷,如果跨块的话,没有重置那它的和肯定不会是最大的。

2  该目标序列在序列块中间,且目标序列的开头并不是序列的第一个。由于序列块前k个数的和不会小于0,所以这样肯定没有从第一个开始来的大。

————————————————————————————————————————————————

5.第五种是动态规划 O(n)

dp做法是很普遍的做法,只要想出状态转移方程就可以很快做出来了。

状态转移方程:sum[i] = max{sum[i-1]+a[i],a[i]}. (sum[i]记录以a[i]为子序列末端的最大连续和。)在dp的过程中便可以更新sum数组的最大值以及两个边界。

其实完全可以不用开数组,累计sum直到sum + a < a,把sum赋值为a,更新最大值就行了。你会发现这跟第4种方法是一样的。。。只是判断条件不一样,一个是sum <= 0一个是sum + a < a。。。(其实是一样的。。。)所以复杂度和第四种是一样的都是O(n)。

————————————————————————————————————————————————

6.第六种是第二种累计求和 O(n)

(感谢@shuangde800 大神的指导)

这种方法跟第4种一样是累计求和。我们可以发现,连续子序列的和其实就是 (到尾端的和)-(到首端-1的和),要让这个式子最大其实可以让(到首端-1的和)尽量的小,我们可以遍历数组,维护(到首端-1的和)的最小值。

理解起来就是遍历的时候,找出以当前位置为尾端,以此前的某一点为首端的 和最大的子序列,然后更新最大值。(其实这个思想和第五种方法是异曲同工的)

由于只需要一遍遍历,所以复杂度为O(n)。

===============================================================

伪代码:

由于下面的Solution里面我的代码太挫,于是先贴出伪代码。

1.暴力 O(n^3):

  1. max←(-∞)
  2. for i←1 to len do
  3. for j←i to len do
  4. sum←0
  5. // 求和
  6. for k←i to j
  7. sum←sum+arr[i]
  8. end for
  9. // 更新最大值
  10. if sum>max then
  11. max←sum
  12. end if
  13. end for
  14. end for
  15. return max

2.预处理 O(n^2):

  1. // 记录前i项和
  2. sum[0]←0
  3. for i←1 to len do
  4. sum[i]←sum[i-1]+arr[i]
  5. end for
  6. // 枚举首尾
  7. max←(-∞)
  8. for i←1 to len do
  9. for j←i to len do
  10. // 更新最大值
  11. if sum[j]-sum[i-1]>max then
  12. max←sum[j]-sum[i-1]
  13. end if
  14. end for
  15. end for
  16. return max

3.分治算法 O(nlogn)

  1. function maxsum(arr,left,right)
  2. // 只有一个元素就返回
  3. if right-left=1 then
  4. return
  5. end if
  6. // 划分[left,mid)和[mid,left)分治
  7. mid←x+(y-x)/2
  8. L←maxsum(arr,left,mid)
  9. R←maxsum(arr,mid,left)
  10. if L>R then
  11. max←L
  12. else
  13. max←R
  14. end if
  15. // 求出处于中间的连续子序列最大和
  16. // 先求从中间向左最大值
  17. tempSum←0
  18. leftSum←arr[m-1]
  19. for i←m-1 downto right do
  20. tempSum←tempSum+arr[i]
  21. if leftSum<tempSum then
  22. leftSum←tempSum
  23. end if
  24. end for
  25. // 再求从中间向右最大值
  26. tempSum←0
  27. rightSum←arr[m]
  28. for i←m to left do
  29. tempSum←tempSum+arr[i]
  30. if rightSum<tempSum then
  31. rightSum←tempSum
  32. end if
  33. end for
  34. midSum←leftSum+rightSum
  35. if max<midSum then
  36. max←midSum
  37. end if
  38. return max
  39. end function

4.累积遍历算法 O(n)

  1. sum←0
  2. max←arr[0]
  3. for i←1 to len do
  4. if sum<=0 then
  5. sum←a[i]
  6. end if
  7. if max<sum then
  8. max←sum
  9. end if
  10. end for
  11. return max

5.动态规划 O(n)

  1. sum←(-∞)
  2. max←(-∞)
  3. for i←1 to len do
  4. if sum+arr[i]<arr[i] then
  5. sum←arr[i]
  6. else
  7. sum←sum+arr[i]
  8. end if
  9. if max<sum then
  10. max←sum
  11. end if
  12. end for
  13. return max

6.第二种累计求和 O(n)

  1. max←-INF
  2. recMin←0
  3. for i←1 to len do
  4. sum←sum+arr[i]
  5. temp←sum-recMin
  6. // 更新max
  7. if temp>max then
  8. max←temp
  9. end if
  10. // 更新recMin
  11. if recMin>sum then
  12. recMin←sum
  13. end if
  14. end for

===============================================================

Solution:

hdu这两题都是要求边界的。

代码如下:

hdu 1003:

题意:求出给出序列中和最大的连续子序列,以及该序列的左边界和右边界

累积遍历算法(会TLE)

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          3.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-28 01:12:27
  7. *   Descripton:    max sum of continuous sequence, simulation
  8. */
  9. #include <cstdio>
  10. #define rep(i, n) for (int i = 0; i < (n); i++)
  11. const int MAXN = 100100;
  12. int k, a[MAXN];
  13. int main() {
  14. int n;
  15. scanf("%d", &n);
  16. rep(cas, n) {
  17. scanf("%d", &k);
  18. rep(i, k) scanf("%d", &a[i]);
  19. int res = a[0], rl = 0, rr = 0;
  20. int sum = 0, l = 0;
  21. rep(i, k) {
  22. if (sum < 0) {
  23. sum = a[i];
  24. l = i;
  25. }
  26. else
  27. sum += a[i];
  28. if (res < sum) res = sum, rl = l, rr = i;
  29. }
  30. if (cas) printf("\n");
  31. printf("Case %d:\n", cas + 1);
  32. printf("%d %d %d\n", res, rl + 1, rr + 1);
  33. }
  34. return 0;
  35. }

动态规划

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          4.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-28 15:23:30
  7. *   Descripton:    max sum of continuous sequence, dp
  8. */
  9. #include <cstdio>
  10. #define rep(i, n) for (int i = 0; i < (n); i++)
  11. int main() {
  12. int l, R, L, Max, sum, k, t, cas, n;
  13. scanf("%d", &n);
  14. rep(cas, n) {
  15. sum = Max = -0xfffffff;
  16. scanf("%d", &k);
  17. rep (i, k) {
  18. scanf("%d", &t);
  19. if (sum + t < t) sum = t, l = i;
  20. else sum += t;
  21. if (Max < sum) Max = sum, L = l, R = i;
  22. }
  23. if (cas) printf("\n");
  24. printf("Case %d:\n", cas + 1);
  25. printf("%d %d %d\n", Max, L + 1, R + 1);
  26. }
  27. return 0;
  28. }

第二种累计求和

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          6.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-29 00:46:25
  7. *   Descripton:    o(n)
  8. */
  9. #include <cstdio>
  10. int n, a[1000000];
  11. int max, recMin, sum, l, recL, recR;
  12. int main() {
  13. int k;
  14. scanf("%d", &k);
  15. for (int cas = 1; cas <= k; cas++) {
  16. scanf("%d", &n);
  17. for (int i = 0; i < n; i++) scanf("%d", &a[i]);
  18. max = -0xfffffff;
  19. sum = 0;
  20. recMin = 0;
  21. l = -1;
  22. recL = -1, recR = 0;
  23. for (int i = 0; i < n; i++) {
  24. sum += a[i];
  25. if (sum - recMin > max) {
  26. max = sum - recMin;
  27. recL = l;
  28. recR = i;
  29. }
  30. if (recMin > sum) {
  31. recMin = sum;
  32. l = i;
  33. }
  34. }
  35. if (cas - 1) printf("\n");
  36. printf("Case %d:\n%d %d %d\n", cas, max, recL + 2, recR + 1);
  37. }
  38. return 0;
  39. }

————————————————————————————————————————————————

hdu 1231:

题意:求连续子序列最大和及上下界的值,输出还得处理下

预处理

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          hdu1231.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-27 17:06:16
  7. *   Descripton:    hdu1231, max sum of continuous sequence, pre, O(n^2)
  8. */
  9. #include <cstdio>
  10. #define rep(i, n) for (int i = 0; i < (n); i++)
  11. #define repf(i, a, b) for (int i = (a); i <= (b); i++)
  12. const int MAXN = 10010;
  13. int k, s[MAXN], a[MAXN];
  14. int main() {
  15. while (scanf("%d", &k) && k) {
  16. s[0] = 0;
  17. repf(i, 1, k) {
  18. scanf("%d", &a[i]);
  19. s[i] = s[i - 1] + a[i];
  20. }
  21. int ans, l, r;
  22. ans = l = r = s[1];
  23. repf(i, 1, k) repf(j, i, k)
  24. if (ans < s[j] - s[i - 1]) {
  25. ans = s[j] - s[i - 1];
  26. l = a[i];
  27. r = a[j];
  28. }
  29. if (ans >= 0)
  30. printf("%d %d %d\n", ans, l, r);
  31. else
  32. printf("0 %d %d\n", a[1], a[k]);
  33. }
  34. return 0;
  35. }

分治思想

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          2_hdu1231.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-27 17:18:49
  7. *   Descripton:    hdu1231, max sum of continuous sequence, partition
  8. */
  9. #include <cstdio>
  10. #define rep(i, n) for (int i = 0; i < (n); i++)
  11. const int MAXN = 10010;
  12. struct ANS {
  13. int sum;
  14. int l, r;
  15. ANS() {}
  16. ANS(int a, int b, int c) : sum(a), l(b), r(c) {}
  17. };
  18. ANS maxsum(int* a, int x, int y) {
  19. int m, v, L, R, l, r;
  20. ANS M(a[x], a[x], a[x]), t1, t2;
  21. if (y - x == 1)
  22. return M;
  23. m = x + (y - x) / 2;
  24. t1 = maxsum(a, x, m);
  25. t2 = maxsum(a, m, y);
  26. if (t1.sum >= t2.sum) M = t1;
  27. else M = t2;
  28. v = 0; L = a[m - 1]; l = a[m - 1];
  29. for (int i = m - 1; i >= x; i--){
  30. v += a[i];
  31. if (v > L) L = v, l = a[i];
  32. }
  33. v = 0; R = a[m]; r = a[m];
  34. for (int i = m; i < y; i++) {
  35. v += a[i];
  36. if (v > R) R = v, r = a[i];
  37. }
  38. if (M.sum > (L + R))
  39. return M;
  40. return ANS(L + R, l, r);
  41. }
  42. int k, a[MAXN];
  43. int main() {
  44. while (scanf("%d", &k) && k) {
  45. rep(i, k) scanf("%d", &a[i]);
  46. ANS ans = maxsum(a, 0, k);
  47. if (ans.sum >= 0)
  48. printf("%d %d %d\n", ans.sum, ans.l, ans.r);
  49. else
  50. printf("0 %d %d\n", a[0], a[k - 1]);
  51. }
  52. return 0;
  53. }

累积遍历算法

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          3.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-28 01:12:27
  7. *   Descripton:    max sum of continuous sequence, simulation
  8. */
  9. #include <cstdio>
  10. #define rep(i, n) for (int i = 0; i < (n); i++)
  11. const int MAXN = 10010;
  12. int k, a[MAXN];
  13. int main() {
  14. while (scanf("%d", &k) && k) {
  15. rep(i, k) scanf("%d", &a[i]);
  16. int res = a[0], rl = a[0], rr = a[k - 1];
  17. int sum = 0, l = 0;
  18. rep(i, k) {
  19. if (sum <= 0) {
  20. sum = a[i];
  21. l = a[i];
  22. }
  23. else
  24. sum += a[i];
  25. if (res < sum) res = sum, rl = l, rr = a[i];
  26. }
  27. if (res >= 0)
  28. printf("%d %d %d\n", res, rl, rr);
  29. else
  30. printf("0 %d %d\n", a[0], a[k - 1]);
  31. }
  32. return 0;
  33. }

动态规划

  1. /*
  2. *   Author:        illuz <iilluzen@gmail.com>
  3. *   Blog:          http://blog.csdn.net/hcbbt
  4. *   File:          4.cpp
  5. *   Lauguage:      C/C++
  6. *   Create Date:   2013-08-28 15:11:46
  7. *   Descripton:    max sum of continuous sequence, dp
  8. */
  9. #include <cstdio>
  10. #define rep(i, n) for (int i = 0; i < (n); i++)
  11. int main() {
  12. int l, R, L, Max, sum, k, t;
  13. while (scanf("%d", &k) && k) {
  14. sum = Max = -0xfffffff;
  15. rep (i, k) {
  16. scanf("%d", &t);
  17. if (sum + t < t) sum = t, l = t;
  18. else sum += t;
  19. if (Max < sum) Max = sum, L = l, R = t;
  20. }
  21. if (Max >= 0)
  22. printf("%d %d %d\n", Max, L, R);
  23. else
  24. printf("0 %d %d\n", L, t);
  25. }
  26. return 0;
  27. }

第二种累计求和

    1. /*
    2. *   Author:        illuz <iilluzen@gmail.com>
    3. *   Blog:          http://blog.csdn.net/hcbbt
    4. *   File:          6.cpp
    5. *   Lauguage:      C/C++
    6. *   Create Date:   2013-08-29 00:08:52
    7. *   Descripton:    o(n)
    8. */
    9. #include <cstdio>
    10. int n, a[1000000];
    11. int max, recMin, sum, l, recL, recR;
    12. int main() {
    13. while (scanf("%d", &n) && n) {
    14. for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    15. max = -0xfffffff;
    16. sum = 0;
    17. recMin = 0;
    18. l = -1;
    19. recL = -1, recR = 0;
    20. for (int i = 0; i < n; i++) {
    21. sum += a[i];
    22. if (sum - recMin > max) {
    23. max = sum - recMin;
    24. recL = l;
    25. recR = i;
    26. }
    27. if (recMin > sum) {
    28. recMin = sum;
    29. l = i;
    30. }
    31. }
    32. if (max >= 0)
    33. printf("%d %d %d\n", max, a[recL + 1], a[recR]);
    34. else
    35. printf("0 %d %d\n", a[0], a[n - 1]);
    36. }
    37. return 0;
    38. }

【ToReadList】六种姿势拿下连续子序列最大和问题,附伪代码(以HDU 1003 1231为例)(转载)的更多相关文章

  1. HDU-1231 简单dp,连续子序列最大和,水

    1.HDU-1231 2.链接:http://acm.hdu.edu.cn/showproblem.php?pid=1231 3.总结:水 题意:连续子序列最大和 #include<iostre ...

  2. 连续子序列最大和的O(NlogN)算法

    对于一个数组,例如:int[] a = {4,-3,5,-2,-1,2,6,-2}找出一个连续子序列,对于任意的i和j,使得a[i]+a[i+1]+a[i+2]+.......+a[j]他的和是所有子 ...

  3. codevs2622数字序列( 连续子序列最大和O(n)算法)

    /* 算法描述:维护一个s[p]表示累加和 并且更新最大值ans 如果s[p]<0 则从p+1重新累加 证明:设某个区间的起点和终点分别为s t 分两种情况 1.t<p:设s2表示1到s的 ...

  4. 动态规划:最大连续子序列乘积 分类: c/c++ 算法 2014-09-30 17:03 656人阅读 评论(0) 收藏

    题目描述: 给定一个浮点数序列(可能有正数.0和负数),求出一个最大的连续子序列乘积. 分析:若暴力求解,需要O(n^3)时间,太低效,故使用动态规划. 设data[i]:第i个数据,dp[i]:以第 ...

  5. hdu 1024(最大和连续子序列增强版)

    题意:最大和连续子序列的增强版,要求从一序列中取出若干段,这些段之间不能交叉,使得和最大并输出. 分析:用dp[i][j]表示前j个数取出i段得到的最大值,那么状态转移方程为dp[i][j]=max( ...

  6. 连续子序列的最大和 牛客网 剑指Offer

    连续子序列的最大和 牛客网 剑指Offer 题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量 ...

  7. [HDOJ1231]最大连续子序列

    混了好几个地方的博客,还是觉得博客园比较靠谱,于是决定在这里安家落户了.本人本科生一个,希望各位巨巨多多指教~ Hello World! 单独一个象征性的问候实在是太low了,还是决定来点实质性的.. ...

  8. DP专题训练之HDU 1231 最大连续子序列

    Description 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j < ...

  9. HDU 1231 最大连续子序列(水题)

    题目链接: 传送门 最大连续子序列 Time Limit: 1000MS     Memory Limit: 32768 K Description 给定K个整数的序列{ N1, N2, ..., N ...

随机推荐

  1. Simple implementation and results of genetic algorithm.

    This experiment was done for the final assignment of my Professional English class. This part has be ...

  2. 微信小程序之组件的集合(三)

    看看音乐播放组件是如何实现完成的音乐的播放的!!! 一.音乐music组件的开发 1.页面以及页面样式的开发 // music组件页面开发 <view hidden="{{hidden ...

  3. Crontab 入门

    参考网址: http://www.centoscn.com/CentOS/help/2014/0820/3524.html 简单命令 service crond restart //重启crontab ...

  4. 学习写Js的动画

    说起前端,要说动画是最有乐子的东西了.玩好动画一定会很轻易的享受到前端的乐趣. 这里我不会讲述什么css3 的 transform animation keyframes,也不会讲述jquery的an ...

  5. 标记扩展和 WPF XAML

      本主题介绍 XAML 的标记扩展概念,包括其语法规则.用途以及底层的类对象模型. 标记扩展是 XAML 语言以及 XAML 服务的 .NET 实现的常规功能. 本主题专门详细论述了用于 WPF X ...

  6. jeecms之全文检索

    需要在后台生成检索,如下: . 这样,在首页进行搜索的时候才会显示如下: 注意,一定要先生成索引,才能进行全文检索.  

  7. 关于springmvc 只能在index.jsp页面显示图片的处理办法jsp页面无法显示图片

    首先,已经配置好了mvc对静态资源的处理 只有index,jsp可以显示图片 其他页面同样的代码则不显示 后来折腾了半天,发现 index是static的父目录的级别文件 可以向下访问 但是其他的js ...

  8. jnhs中国省市县区mysql数据表不带gps坐标

    1.查省 SELECT * FROM china WHERE china.Pid=0 2.查市 SELECT * FROM chinaWHERE china.Pid=330000 3.查区 SELEC ...

  9. Java 类与类之间的调用

    方法1. 新建一个类. 然后在调用类中先进行被调用类实例化,然后通过实例化的对象访问. 例如: //先定义一个类 import static java.lang.System.out; public ...

  10. mybatis的第一个程序

    程序结构图: 表结构: 创表sql: CREATE TABLE `users` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `username` varch ...