最近开始复习数据结构,准备把《数据结构与算法分析:C语言描述》这本书重刷一遍,顺便做点笔记。
GitHub地址:https://github.com/Chaomin702/Algorithm.git
问题描述
最大公共子序列求和(Maximum Contiguous Subsequence Sum):
给定整数 A1,A2,...,AN (可能有负数),求 ∑jk=iAk 的最大值(为方便起见,如果所有整数均为负数,则最大子序列和为0)。
比如样例集合为 {−2,11,−4,13,−5,−2} ,最长子序列和应为20。
暴力求解
最直接可以想到的一种解法就是:枚举所有的公共序列,计算最大和即可。即:
maxsj,k=∑i=jkai(0⩽j,k<N)
思路很简单,直接给出代码。
int MaxSubsequenceSumForce(const int A[], int N){
int maxSum = 0, thisSum = 0;
for (int i = 0; i < N; i++){
thisSum = 0;
for (int j = i; j < N; j++){
thisSum += A[j];
if (thisSum >maxSum)
maxSum = thisSum;
}
}
return maxSum;
}
动态规划
设 Fi 为以元素 i 结尾的连续最大子序列的和。
那么对于第 i 个元素来说,要形成连续的最大子序列和,只和相邻的前一个元素有关。如果 Ai 加上以 Ai−1 结尾的最大连续子序列和 Fi−1 后为负数,那么 Fi=0 ,否则 Fi=Fi−1+Ai 。
也就是:
Fi=max(0,Fi−1+Ai)
其中
F0=0 。
下面给个求解过程来感受一下。
i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
Ai | -2 | 11 | -4 | 13 | -5 | -2 |
Fi | 0 | 11 | 7 | 20 | 15 | 13 |
上述解法的正确性很容易用数学归纳法证明,在此略过。下面附上代码:
int MaxSubsequenceSumDP1(const int A[], int N){
int f = 0, maxSum = 0;
for (int i = 0; i < N; i++){
f += A[i];
if (f < 0)
f = 0;
if (maxSum < f)
maxSum = f;
}
return maxSum;
}
分而治之
轮到Divide and Conquer
这个强大的工具出场了。
其实,最大连续序列和可能会在三处出现:
- 输入数据的前半部分
- 输入数据的后半部分
- 跨越输入数据的中部而占据左右各半部分。
其中前两种情况可以递归求解,第3种情况可以通过求出前半部分的最大和(包含前半部分的最后一个元素)和后半部分的最大和(包含后半部分的第一个元素)而得到,然后将这两个和加在一起。
最后,选出这三个最大和的最大者即可。
好,下面附上代码。
int MaxSubSumDC(const int A[], int l, int h){
if (h - l < 1){
if (A[l]< 0)
return 0;
else
return A[l];
}
int centre = (l + h) / 2;
int leftMax = MaxSubSumDC(A, l, centre);
int rightMax = MaxSubSumDC(A, centre + 1, h);
int centreLeftMax = 0, centreLeftSum = 0;
for (int i = centre; i>=l; i--){
centreLeftSum += A[i];
if (centreLeftSum > centreLeftMax){
centreLeftMax = centreLeftSum;
}
}
int centreRightMax = 0, centreRightSum = 0;
for (int i = centre + 1; i <= h; i++){
centreRightSum += A[i];
if (centreRightSum > centreRightMax)
centreRightMax = centreRightSum;
}
int centreMax = centreLeftMax + centreRightMax;
return max(max(leftMax, rightMax), centreMax);
}
时间复杂度分析
暴力法和动态规划的时间复杂付毋庸置疑,分别是 O(n2),O(n) 。
分治法略微有些复杂:
{T(1)=1T(n)=T(n/2)+n
也就是
O(nlogn)