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

Posted zhenglijie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暴力+分治+贪心+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是根据自己的理解写的,如有不对,请指正谢谢。

 

 

 

 

 

 




 




以上是关于暴力+分治+贪心+DP:最大子序列和的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P1115 最大子段和(dp 贪心)

动态规划:最大连续子序列乘积

数组---最大子序列和

noip pascal 谁有好的贪心总结和dp总结发个网址看看

0.分治永远大于顺序?关于最大子序列和问题的思考

分治法求最大子序列