• 暴力+分治+贪心+DP:最大子序列和


    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4],
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6

    暴力:暴力列举所有可能的连续子数组,算法复杂度O(N^3)
    算法1:

     1 int MaxSubseqSum1(int A[], int N)
     2 {
     3     int ThisSum, MaxSum = 0;
     4     int i,j,k;
     5 
     6     for (i = 0; i < N; i++)  //i对应子列左端位置
     7     {
     8         for (j = i; j < N; j++)  //j对应子列右端位置
     9         {
    10             ThisSum = 0;
    11             for (k = i; k <= j; k++) //一段子列的和
    12             {
    13                 ThisSum += A[k];
    14             }
    15             if(ThisSum > MaxSum)
    16                 MaxSum = ThisSum; //更新
    17         }
    18     }
    19     return MaxSum;
    20 }

    每次从i加到j,我们都必须要经历k循环,i+(i+1)...j,所以每次j循环后都要经历一个k循环从i加到j,想想完全没有必要,可以直接在前一个子序
    列的基础上加一个元素,
    所以k循环是没有必要的。
    因此优化算法在相同的i不同的j只需要在j-1次的循环的基础上累加一项即可,算法复杂度更新为O(N^2)
    算法2:


     1 int MaxSubseqSum2(int A[], int N)
     2 {
     3     int ThisSum, MaxSum = 0;
     4     int i,j,k;
     5 
     6     for (i = 0; i < N; i++)  //i对应子列左端位置
     7     {
     8         ThisSum = 0; 
     9         for (j = i; j < N; j++)  //j对应子列右端位置
    10         {
    11             ThisSum += A[k];  //在上一个子序列和的基础上加一个数
    12             
    13             if(ThisSum > MaxSum)
    14                 MaxSum = ThisSum;
    15         }
    16     }
    17     return MaxSum;
    18 }
    
    

    算法3:分治把大问题拆成小问题,然后逐个解决,最后合并起来。

    把数组一分为二,分别递归(即左右两边再分成小的左右两边)的去解决左右两边问题,得到两边的最大子列和,还有一种情况跨越边界的最大子列和,然后想要的结果就是这三个数之间的最大的那个数。算法复杂度O(NlogN)

     1 int Max3( int A, int B, int C )
     2 { /* 返回3个整数中的最大值 */
     3     return A > B ? A > C ? A : C : B > C ? B : C;
     4 }
     5  
     6 int DivideAndConquer( int List[], int left, int right )
     7 { /* 分治法求List[left]到List[right]的最大子列和 */
     8     int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
     9     int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/
    10  
    11     int LeftBorderSum, RightBorderSum;
    12     int center, i;
    13  
    14     if( left == right )  { /* 递归的终止条件,子列只有1个数字 */
    15         if( List[left] > 0 )  return List[left];
    16         else return 0;
    17     }
    18  
    19     /* 下面是"分"的过程 */
    20     center = ( left + right ) / 2; /* 找到中分点 */
    21     /* 递归求得两边子列的最大和 */
    22     MaxLeftSum = DivideAndConquer( List, left, center );
    23     MaxRightSum = DivideAndConquer( List, center+1, right );
    24  
    25     /* 下面求跨分界线的最大子列和 */
    26     MaxLeftBorderSum = 0; LeftBorderSum = 0;
    27     for( i=center; i>=left; i-- ) { /* 从中线向左扫描 */
    28         LeftBorderSum += List[i];
    29         if( LeftBorderSum > MaxLeftBorderSum )
    30             MaxLeftBorderSum = LeftBorderSum;
    31     } /* 左边扫描结束 */
    32  
    33     MaxRightBorderSum = 0; RightBorderSum = 0;
    34     for( i=center+1; i<=right; i++ ) { /* 从中线向右扫描 */
    35         RightBorderSum += List[i];
    36         if( RightBorderSum > MaxRightBorderSum )
    37             MaxRightBorderSum = RightBorderSum;
    38     } /* 右边扫描结束 */
    39  
    40     /* 下面返回"治"的结果 */
    41     return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
    42 }
    43  
    44 int MaxSubseqSum3( int List[], int N )
    45 { /* 保持与前2种算法相同的函数接口 */
    46     return DivideAndConquer( List, 0, N-1 );
    47 }

    算法4:贪心算法(在线处理算法)

    每输入一个数据,进行即时处理,在任何一个地方停止输入,算法都能得到正确的解,即总是做出在当前看来最好的选择。

    只需遍历一遍数组,算法复杂度为O(N)。

     1 int MaxSubseqSum4(int A[], int N)
     2 {
     3     int i;
     4     int ThisSum, MaxSum;
     5     ThisSum = MaxSum = 0;
     6 
     7     for (i = 0; i < N; i++)  //向右累加
     8     {
     9         ThisSum += A[i];  
    10         if (ThisSum > MaxSum)
    11             MaxSum = ThisSum;  //发现更大则更新
    12         else if (ThisSum > MaxSum) //如果当前子序列为负,因为它不能使后边子列和增大
    13             ThisSum = 0; //直接放弃累加
    14     }
    15     return MaxSum;
    16 }

    法5:动态规划(DP)

    不断更新dp[i]中的值,表示A数组中以A[i]为结尾的最大子序列和,例如A = [2,3,-6,2,4],则dp = [2,5,-1,2,6],则dp数组中的最大值就是最大子序列和就是6.

    只需要遍历一遍数组,算法复杂度O(N)

     1 int MaxSubseqSum1(int A[], int N)
     2 {
     3     int i;
     4     int dp[N];
     5     int ThisSum = 0;
     6     dp[0] = A[0];
     7     for (i = 1; i < N; i++)
     8     {
     9         if (dp[i]<0 || (i==1 && dp[0]<0)) //如果A[0]就小于0则它并不能使后边序列增大所以不累加,或者后边的子序列和中出现负值
    10         {
    11             dp[i] = A[i];
    12         }
    13         else
    14         {
    15             dp[i] = dp[i - 1] + A[i];
    16         }
    17     }
    18 
    19     return Max.dp[i];
    20 }

    DP是根据自己的理解写的,如有不对,请指正谢谢。







  • 相关阅读:
    51Nod-1002-数塔取数问题
    Android Studio: Application Installation Failed解决方案
    1001 数组中和等于K的数对——51NOD
    51Nod-1005 大数加法
    aiml_入门学习
    vim使用进阶
    学习寒小阳的博客之统计机器翻译
    安装cywin
    TF-IDF学习
    Java文件读写操作
  • 原文地址:https://www.cnblogs.com/ZhengLijie/p/12533763.html
Copyright © 2020-2023  润新知