zoukankan      html  css  js  c++  java
  • [数字技巧]最大连续子序列和

      最大连续子序列和这个问题是一个比较常见的问题,出现在很多公司的面试笔试中。题目大概是这样描述的:

      输入一个整形数组,数组中有正数也有负数,数组中连续一个或多个组成一个子序列,每个子序列都有一个和,求所有子序列的和的最大值。

      一、暴力枚举

      最暴力的解法是枚举起点和终点(或长度),连同求和的那个步骤,共三个for循环,复杂度是O(n3)。代码示例如下:

     1 #include <cstdio>
     2 #include <cstdlib>
     3 #include <limits.h>
     4 
     5 using namespace std;
     6 
     7 int n;
     8 int arr[1001];
     9 long maxRes;
    10 int start;
    11 int end;
    12 long solve1(int *arr,int n)
    13 {
    14      long max = INT_MIN;
    15      for(int i=0; i<n; i++){
    16           for(int j=i; j<n; j++){
    17                long total = 0;
    18                for(int k=i; k<=j; k++)
    19                     total += arr[k];
    20                if(total>max)
    21                     max = total;     
    22           }        
    23      }
    24      return max;
    25 }
    26 int main()
    27 {
    28      freopen("maxSub.in","r",stdin);
    29      freopen("maxSub.out","w",stdout);
    30      
    31      while(scanf("%d",&n)!=EOF && n>0){
    32           for(int i=0; i<n; i++){
    33                scanf("%d",&arr[i]);
    34           }
    35           maxRes = solve1(arr,n);
    36           printf("%ld
    ",maxRes);                          
    37      }
    38      return 0;    
    39 }
    View Code

      max表示从a[i]到a[j]的和。

      二、暴力枚举的改进  

      显然这样的算法是非常丑陋的,是否存在可优化的地方呢?一个最明显的冗余计算便是:arr[0]+arr[1]+...+arr[i]被计算了n-i-1次。实际上,第三个求和的过程是多余的,我们在枚举终点的过程中,即可进行求和运算,每加一个新数,即与最大值比较。这种做法可以省掉一个for循环,时间复杂度降到了O(n2),代码如下:

     1 int solve2(int *arr,int n)
     2 {
     3      long max = INT_MIN;
     4      for(int i=0; i<n; i++){
     5           long total = 0;
     6           for(int j=i; j<n; j++){               
     7                total += arr[j];
     8                if(total>max)
     9                     max = total;     
    10           }        
    11      }
    12      return max;    
    13 }
    View Code

      三、巧妙的解法

      在做优化之前,我们应该有三个认识:

      1、以上的都枚举了起点和终点,而题目中只关心最大和,是不是可以优化。

      2、假设arr[i]是负数,那么最大子序列肯定不是以arr[i]为起点,因为以arr[i+1]为起点的子序列和肯定不会比以a[i]小。

      3、假设arr[i]...arr[j]的和是负数,那么最大子序列完全可以从arr[j+1]开始,原因同上。

      有了以上想法,我们不难写出以下代码:

     1 int solve3(int *arr,int n)
     2 {
     3      long max = 0;
     4      long total = 0;
     5      for(int i=0; i<n; i++){
     6           total += arr[i];
     7           if(total > max)
     8                max =  total;
     9           else if(total < 0)
    10                total = 0;   
    11      }
    12      return max;    
    13 }
    View Code

      这是一个线性时间常量空间的算法,最大的特点是一旦读入arr[i]就被立即处理,不再被记忆,这就是所谓的联机算法。

      四、巧妙解法的两个改进

        改进一、这种算法在数组中所有的数为负数时,是得不到正确结果的,通过对负数单独讨论,满足全负数情形;

        改进二、在计算的过程中忽略了起始位置和终点位置,可通过做两次标记,保存起点和终点位置。

        这里直接给出一道类似的题目,具体不在赘述。

        http://www.cnblogs.com/codershell/p/3307795.html

     
  • 相关阅读:
    datatables 自定义排序
    nginx 高并发配置参数
    查看域名https证书到期时间
    Python七大原则,24种设计模式
    scrapy RuntimeError: maximum recursion depth exceeded while calling a Python object 超出python最大递归数异常
    scrapy 用pycharm调试
    修改Mysql 字符集,支持Emoji表情
    redis资料
    mysql 开启日志与性能调优
    python 版本号比较 重载运算符
  • 原文地址:https://www.cnblogs.com/codershell/p/3302423.html
Copyright © 2011-2022 走看看