Kadane算法

又一个经典问题,对于一个包含负值的数字串array[1...n],要找到他的一个子串array[i...j](0<=i<=j<=n),使得在array的所有子串中,array[i...j]的和最大。(array[]大概有1百万个数)

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include<stdio.h>  
  2. int main()  
  3. {  
  4.     int t,n,x,i,j,sum,max;  
  5.     scanf("%d",&t);  
  6.     for(i=1;i<=t;i++){  
  7.         scanf("%d",&n);  
  8.         max=-101;  
  9.         sum=0;  
  10.         for(j=1;j<=n;j++){  
  11.             scanf("%d",&x);  
  12.             sum+=x;  
  13.             if(sum>max)  
  14.                 max=sum;  
  15.             if(sum<0)  
  16.                 sum=0;  
  17.         }  
  18.         printf("%d\n",max);  
  19.     }  
  20.     return 0;  
  21. }  

总结:Kadane算法。(Kadane算法正确性的证明以及解题思路详见:最大子串和

(1)子串和子序列之间的区别。子串是指数组中连续的若干个元素,而子序列只要求各元素的顺序与其在数组中一致,而没有连续的要求。对于一个元素数为n的数组,其含有2^n个子序列和n(n+1)/2个子串。如果使用穷举法,则至少需要O(n^2)的时间才能得到答案。卡耐基梅隆大学的Jay Kadane的给出了一个线性时间算法,我们就来看看,如何在线性时间内解决最大子串和问题。

(2)Kadane算法的执行流程,从头到尾遍历目标数组,将数组分割为满足上述条件的子串,同时得到各子串的最大前缀和,然后比较各子串的最大前缀和,得到最终答案。我们以array={−2, 1, −3, 4, −1, 2, 1, −5, 4}为例,来简单说明一下算法步骤。通过遍历,可以将数组分割为如下3个子串(-2),(1,-3),(4,-1,2,1,-5,4),这里对于(-2)这样的情况,单独分为一组。各子串的最大前缀和为-2,1,6,所以目标串的最大子串和为6。(算法思想看不懂的话,就跟着程序走一遍,读完程序后就可以理解这个算法了)。

(3)当全部元素均为负数时,此写法依然成立,此时最大的子串和就是全部元素中的最大值。






(2014.7.7)今天在网上浏览最大子矩阵和的时候,又看到了一种最大子串和的解释,而且讲的还不错: 最大子矩阵和、最大子串和问题

1、直接穷举法。当n很大时,时间复杂度太高,不可行(需改进)   (这里就不再写代码了)

2、带记忆的递推法。时间复杂度较第一种方法已经有了很大的改进,但是依然比较高(当n特别大时),时间复杂度是n*n(还需改进);还有一点就是边界初始化问题,见注释信息(b[-1])    (附上此种方法代码,依然超时)

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include<iostream>  
  2. #include<cstdio>  
  3. using namespace std;  
  4.   
  5. int a[1000005],b[1000005]; //a[]为原数组,b[j]为a[0]到a[j]的和  
  6.   
  7. int main()  
  8. {  
  9.     int t,n,i,j,sum,max;  
  10.     scanf("%d",&t);  
  11.     while(t--){  
  12.         scanf("%d",&n);  
  13.         for(i=0;i<n;i++)  
  14.             scanf("%d",&a[i]);  
  15.         b[0]=a[0];  
  16.         for(i=1;i<n;i++)  
  17.             b[i]=b[i-1]+a[i];  
  18.         max=-101;  
  19.         for(i=0;i<n;i++)  
  20.             for(j=i;j<n;j++){  
  21.                 sum=b[j]-b[i-1]; //语法上没有错误,b[-1]也被访问到了,并参与了运算  
  22.                 if(sum>max)  
  23.                     max=sum;  
  24.             }  
  25.         printf("%d\n",max);  
  26.   
  27.     }  
  28.     return 0;  
  29. }  

3、DP。下面我们来分析一下最大子段和的子结构,令b[j]表示从a[0]~a[j]的最大子段和,b[j]的当前值只有两种情况:

(1) 最大子段一直连续到a[j]    (2) 以a[j]为起点的子段,不知有没有读者注意到还有一种情况,那就是最大字段没有包含a[j],如果没有包含a[j]的话,那么在算b[j]之前的时候我们已经算出来了,注意我们只是算到位置为j的地方,所以最大子断在a[j]后面的情况我们可以暂时不考虑。

由此我们得出b[j]的状态转移方程为:b[j]=max{b[j-1]+a[j],a[j]} ,所求的最大子断和为max{b[j],0<=j<n} 。进一步我们可以将b[]数组用一个变量代替。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include<iostream>  
  2. #include<cstdio>  
  3. using namespace std;  
  4.   
  5. int a[1000005];  
  6.   
  7. int maxSubArray(int a[],int n)  
  8. {  
  9.     int b=0,sum=-10000000;  //sum=INT_MIN ,INT_MIN包含在#include<climits>表示无穷小(相应的也有INT_MAX)  
  10.     for(int i=0;i<n;i++){  
  11.         if(b>0)  
  12.             b+=a[i];  
  13.         else  
  14.             b=a[i];  
  15.         if(b>sum)  
  16.             sum=b;  
  17.     }  
  18.     return sum;  
  19. }  
  20.   
  21. int main()  
  22. {  
  23.     int t,n,i;  
  24.     scanf("%d",&t);  
  25.     while(t--){  
  26.         scanf("%d",&n);  
  27.         for(i=0;i<n;i++)  
  28.             scanf("%d",&a[i]);  
  29.         printf("%d\n",maxSubArray(a,n));  
  30.     }  
  31.     return 0;  
  32. }  


就个人来看,感觉第三种DP跟上面的Kadane算法好像是一样的。恩,,,,,,,,,  再好好琢磨琢磨,体会体会。。。。。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值