问题描述:求一串数列中,子序列最大和;相对应的是最小子序列和问题,类似
算法一:暴力求解,时间复杂度O(N^3),不推荐
算法二:采用“分治”策略,复杂度O(NlogN),体会思想。最大子序列和可能出现在左、中、右。
#include <iostream>
#include <vector>
using namespace std;
int subSeqMax(const int arr[], int n);
int subProMax(const int arr[], int startIndex, int endIndex);
template<typename T>
int length(T& arr)
{
return sizeof(arr) / sizeof(arr[0]);
}
int main()
{
int arr[8] = { -2, 6, -1, 5 ,4 ,-7, 2 ,3 };//答案为14
int n = length(arr);
int subMax = subSeqMax(arr, length(arr));
cout << "最大和:" << subMax << endl;
system("pause");
return 0;
}
int subSeqMax(const int arr[], int n)
{
int startIndex = 0;
int endIndex = n - 1;
int max = subProMax(arr, startIndex, endIndex);
return max;
}
int subProMax(const int arr[], int startIndex, int endIndex)
{
int pivot = (startIndex + endIndex) / 2;
if (startIndex == endIndex)//退出递归的基准条件
{
return arr[startIndex];
}
int leftMax = 0, rightMax = 0;
leftMax = subProMax(arr, startIndex, pivot);
rightMax = subProMax(arr, pivot + 1, endIndex);
//处理最大和在中间的情况
int leftBorderMax = 0, rightBorderMax = 0;
int leftBorderSum = 0, rightBorderSum = 0;
int leftBorderIndex, rightBordIndex;
//从基准开始,求左边最大和和右边最大和
for (int i = pivot; i >= startIndex; i--)
{
leftBorderSum += arr[i];
if (leftBorderMax < leftBorderSum)
leftBorderMax = leftBorderSum;
}
for (int i = pivot + 1; i <= endIndex; i++)
{
rightBorderSum += arr[i];
if (rightBorderSum > rightBorderMax)
rightBorderMax = rightBorderSum;
}
//求最大的一个作为结果
int middleMax = leftBorderMax + rightBorderMax;
int max = 0;
if (middleMax > leftMax)
max = middleMax;
else max = leftMax;
if (max < rightMax)
max = rightMax;
return max;
}
算法三:比较巧妙,一个优点是只对数据进行一次扫描
int maxSubSeqSum(const int arr[], int n)
{
int maxSum, thisSum;
maxSum = thisSum = 0;
for (int i = 0; i < n; i++)
{
thisSum += arr[i];
if (maxSum < thisSum)
maxSum = thisSum;
else if (thisSum < 0)
thisSum = 0;
}
return maxSum;
}
算法四:采用动态规划,复杂度O(N)
很多动态规划算法非常像数学中的递推。我们如果能找到一个合适的递推公式,就能很容易的解决问题。
我们用dp[n]表示以第n个数结尾的最大连续子序列的和,于是存在以下递推公式:dp[n] = max(0, dp[n-1]) + num[n]
仔细思考后不难发现这个递推公式是正确的,则整个问题的答案是max(dp[m]) | m∈[1, N]
。C语言代码如下:
note:存放dp[n]的数组和num[]共用一个,节省了空间,但破坏了输入数组!
//动态规划
#include <stdio.h>
//N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
int N, num[134217728];
int main()
{
//输入数据
cin >> N;
for (int i = 0; i < N; i++)
cin >> num[i];
int ans = 0;
for (int i = 0; i < N; i++)
{
if (num[i] > 0)
num[i + 1] += num[i];
if (num[i + 1] > ans)
ans = num[i + 1];
}
cout << ans << endl;
system("pause");
return 0;
}
这里我们没有创建dp数组,根据递归公式的依赖关系,单独一个num数组就足以解决问题,创建一个一亿长度的数组要占用几百MB的内存!这个算法的时间复杂度是O(N)的,所以它计算一亿长度的序列也不在话下!不过你如果真的用一个这么大规模的数据来测试这个程序会很慢,因为大量的时间都耗费在程序读取数据上了!