- 递归问题常见解题思路
- 明确递归的目的
- 寻找递归的结束条件
- 寻找函数的等价关系式
- 最大子列和问题
问题描述:给定n个整数的序列{a1,a2,------an},寻找函数f(i,j)=max{0,∑(i,j)ak}
算法一:部分穷举法
复杂度:o(n^2) 不推荐
int MaxSubseqSum(int list[], int n){
int i, j;
int Thissum,Maxsum=0;
for(i=0;i<n;i++){
Thissum=0;
for(j=i;j<n;j++){
Thissum+=list[j];
Maxsum=Thissum>Maxum?Thissum:Maxsum;
}
}
return Maxsum;
}
算法二:分而治之
第一步:将序列从中间分成两个子序列
第二步:递归球的两子列的最大与最小值
第三步:从中间开始向两边扫描,寻找最大值。
第四步:s=max{左,中,右}
int Max3(int A, int B, int C){
return A>B?A>C?A:C:B>C?B:C; //返回三个中最大值,也可以用(A>B?A:B)>C?(A>B?A:B):C
}
int DivideAndConquer(int list[], int left, int right){
int MaxLeftBorderSum=0,MaxRightBorderSum=0; //存放中中间往两边的两个最大值
int MaxLeftSum,MaxRightSum; //存放左,右两人子列的最大值
int center,i;
int LeftBoederSum=o,RightBorderSum=0;
//结束条件 (判断是否左右相等)
if(left==right){
if(list[left]>0){
return list[left];
}
else
return 0;
}
center=(left + right)/2;
MaxLeftSum=DivideAndConquer(list[], left,center);
MaxRightSum=DivideAndConquer(list[],center,right);
//取从中间开始的往左边子列最大值
for(i=center; i>=left; i--){
LeftBorderSum + = list[i];
MaxLeftBorderSum = LeftBorderSum > MaxLeftBorderSum ? LeftBorderSum : MaxLeftBorderSum;
}
//取从中间开始的往右边的子列最大值
for(i=center; i<=right; i++){
RightBorderSum + = list[i];
MaxRightBorderSum = RightBorderSum > MaxRightBorderSum ? RightBorderSum : MaxRightBorderSum;
}
}
return Max3(MaxLeftSum , MaxRightSum , MaxLeftBorderSum + MaxRightBorderSum);
分而治之的算法时间复杂度为nlogn
算法三:在线处理
基于:如果整数序列{a1,a2,an}最大子列是{ai,aj}
,那么对于任意的i < = l < = j, 恒有ai+,,,,+al>0
int MaxSubseqSum3(int list[], int n){
int i;
int ThisSum,MaxSum;
ThisSum=MaxSum=0;
for(i=0;i<n;i++){
ThisSum+=list[i];
if(ThisSum>MaxSum){
MaxSum=ThisSum;
}
else if(ThisSum<0){
ThisSum=0;
}
}
return MaxSum;
}
提高效率的关键在于:让计算机记住一些关键结果,避免重复运算