最长子序列问题之系列一
求解最大连续子序列和问题,最直接的办法就是求出每个连续子序列的和,然后找出最大和即可,但这种算法的效率为O(n^2),代码如下,
int LS()
{
int Max=-999999999;
for( int i=0;i<n;i++)
{
int sum=0;
for( int j=i;j<n;j++)
{
sum+=a[j];
if(sum>Max) Max=sum;
}
}
return Max;
}
这种算法适用于数据量比较小的情况。
在这种方法里, 有许多不必要的重合计算,而正是这些不必要的计算影响了程序的执行效率。为了提高算法的效率,尽可能的排除那些不必要的计算过程,可以采用一下算法,此算法效率为O(n).
此算法的改进之处就在于消除了前一个算法出现多次重复的弊端。
//该算法满足连续子序列求和问题:sum>=0的情况
//如果有最大和为负数的情况,可以以特殊情况单独处理即可
for(i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
if(sum>Max)
{
Max=sum;
start=k+1;
end=i+1;
}
if(sum<0)//关键点,消除不必要的重复
{
k=i+1;
sum=0;
}
}
算法思想:
对于每个a[i]而言,在和前一次的sum相加后都会进行一次比较,
if(sum>Max)
{
Max=sum;
start=k+1;
end=i+1;
} , 其中start记录最大连续子序列的起始位置,end记录其结束位置 ,而在如果出现sum<0的情况 ,直接将sum=0; 因为对于任何一个子序列来说,在保证了其sum>=0的前提下,出现负数是不可能的,故可将其直接清0;而此时需对起始位置进行记录,以便下一次如果出现sum>Max的情况,则可为start重新赋值。
此处还有一个需要注意的地方就是0<sum<Max的情况,其实这也好理解,如果0<sum<Max, 则继续执行 sum+=a[i], 在这种情况下不能对sum进行清0,因为sum是一个递增的过程,可能在某一时刻会出现sum>Max的情况。
做题的时候碰到了两种情况:
(1)http://acm.hdu.edu.cn/showproblem.php?pid=1003 上面的过程就是解决这个问题的
(2)http://acm.hdu.edu.cn/showproblem.php?pid=1231
因为上面的出现保证了sum>=0的情况,所以 对上面的算法做一个小处理就ok了,处理如下:
- 排除都为负数的情况
- 排除最大和为0的情况
代码如下,
for(i=0;i<n;i++)
{
cin>>a[i];
if(a[i]<0) count++; //排除都为负数的情况
if(a[i]==0&&i<L) L=i; //排除最大和为0的情况
sum+=a[i];
if(sum>Max)
{
Max=sum;
start=k+1;
end=i+1;
}
if(sum<0)
{
k=i+1;
sum=0;
}
}
if(Max>0) cout<<Max<<" "<<a[start-1]<<" "<<a[end-1]<<endl;
else if(count!=n&&Max==0) cout<<Max<<" "<<a[L]<<" "<<a[L]<<endl;
else cout<<Max<<" "<<a[0]<<" "<<a[n-1]<<endl;