最大子段和问题
给定整数序列,求该序列形如k=ijak的子段和的最大值,1)已知一个简单算法如下:
试分析该算法的时间复杂性。
答:时间复杂度为O(n^2)
2)试用分治算法解最大子段和问题,并分析算法的时间复杂性。
算法思路
需要在序列 中找到一段最优序列,将序列分为和,与最邻近点对问题类似,最优序列所在的位置有以下3种情况:
分治法代码
#include<iostream>
#include<cstdio>
#include<map>
#include<string>
#include<set>
#include<vector>
using namespace std;
using namespace std;
// Q1 最大子段和
class Ques20201025
{
public:
void test() {
vector<int> arr = {-2,11,-4,13,-5,-2};
cout << maxSum(arr,1, arr.size()-1) << endl;
int i, j;
}
private:
// 分治法
int maxSum(vector<int> &arr, int left, int right) {
int sum = 0;
if (left==right) {
sum = arr[left]>0?arr[left]:0;
}
else
{
int mid = left + (right - left) / 2;
// 最优序列在左边和右边时,递归处理
int leftsum=maxSum(arr, left, mid);
int rightsum = maxSum(arr, mid + 1, right);
// 第3种情况的处理,最优序列跨过中心点mid
int s1 = 0; int lefts = 0;// s1
for (int i = mid; i >= left;i--) {
lefts += arr[i];
if (lefts>s1) {
s1 = lefts;
}
}
int s2 = 0;// s1
int rights = 0;
for (int i = mid+1; i <= right; i++) {
rights += arr[i];
if (rights > s2) {
s2 = right;
}
}
sum = s1 + s2;// 加在一起
if (sum < leftsum)sum = leftsum;
if (sum < rightsum)sum = rightsum;
}
return sum;
}
};
int main() {
Ques20201025 qus = Ques20201025();
qus.test();
}
实现效果
时间复杂度
解得T(n)=O(nlogn)
3)试说明最大子段和问题具有最优子结构性质,并设计一个动态规划算法解最大子段和问题。分析算法的时间复杂度。
说明
动态规划代码
// 动态规划法
//设一个b数组,存的是i-j的a[i:j]子串的最大值,则总问题变为max b[j].当b[j-1]>0时,b[j]=b[j-1]+a[j]否则直接a[j]
// b[j]=max{b[j-1]+a[j],a[j]}
int DnMaxSum(vector<int>& arr) {
int n = arr.size() - 1;
int sum = 0,b=0;
for (int i = 1; i <= n; i++) {
if (b > 0) b += arr[i];
else b = arr[i];
if (b > sum) sum = b;
}
return sum;
}
时间复杂度
只有一个for循环,因此算法的时间复杂度和空间复杂度都是O(n).