最大连续子序列和问题

最大连续子序列和问题,第1张

最大连续子序列和问题

目录

题目描述

题目分析

图一乐之超级遍历法

聪明了一些之还是遍历法

递归分治法

动态规划解法

联机算法

写在最后


题目描述

给定一个数字序列,求i, j (1 ≤ i ≤ j ≤ n),使得最大,输出这个最大和。

题目分析

虽然这篇博客旨在讲解动态规划解法,但我们还是先来看看各种解法都有哪些:

图一乐之超级遍历法
#include 
using namespace std;
const int N = 1010;
int num[N];
int MaxSubSequenceSum(const int A[], int N){
	int ThisSum, MaxSum = 0;
	for(int i = 0;i < N; i ++){
		for(int j = i;j < N;j ++){
			ThisSum = 0;
			for(int k = i;k <= j;k ++){
				ThisSum += A[k];
			if(ThisSum > MaxSum){
				MaxSum = ThisSum;
			}	
			}
		}
	}
	return MaxSum;
}
int main(){
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> num[i];
    cout << MaxSubSequenceSum(num, n) << endl;
    return 0;
}

最基础的想法,就是穷举所有可能,时间复杂度

聪明了一些之还是遍历法

第一种方法涉及了太多的重复运算,笨到令人发指的程度。因此我们可以考虑撤掉一个for循环进行优化。

#include 
using namespace std;
const int N = 1010;
int num[N];
int MaxSubSequenceSum(const int A[], int N){
	int ThisSum, MaxSum = 0;
	for(int i = 0;i < N; i ++){
		ThisSum = 0;
        for(int j = i; j  MaxSum)
                MaxSum = ThisSum;
        }
	}
	return MaxSum;
}
int main(){
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> num[i];
    cout << MaxSubSequenceSum(num, n) << endl;
    return 0;
}

现在,时间复杂度变成了。

递归分治法

在问题中,最大连续子序列无非出现在三个地方:

    全部出现在序列的左半边;全部出现在序列的右半边;出现在中部,跨越左右半边

对于前两种情况,我们可以采用递归求解的方式,而第三种情况的最大和可以通过求出左半边的最大和(包含左半边的最后一个元素)加上右半边的最大和(包含右半边的第一个元素)而得到。易知分治法的复杂度为,又因为for循环最多只有一层,所以该算法的复杂度为。

#include 
using namespace std;
const int N = 1010;
int num[N];
int MaxSubSequenceSum(const int A[], int Left, int Right){
	int MaxLeftSum = 0, MaxRightSum = 0;
    int MaxLeftBorderSum = 0, MaxRightBorderSum = 0;
    int LeftBorderSum = 0, RightBorderSum = 0;
    int Center;
    if(Left == Right){
        if(A[Left] > 0) return A[Left];
        else return 0;
    }
    Center = (Right + Left) / 2;
    MaxLeftSum = MaxSubSequenceSum(A, Left, Center);
    MaxRightSum = MaxSubSequenceSum(A, Center + 1, Right);

    for(int i = Center; i >= Left; i --){
        LeftBorderSum += A[i];
        if(LeftBorderSum > MaxLeftBorderSum)
            MaxLeftBorderSum = LeftBorderSum;
    }
    for(int i = Center + 1; i <= Right; i ++){
        RightBorderSum += A[i];
        if(RightBorderSum > MaxRightBorderSum)
            MaxRightBorderSum = RightBorderSum;
    }
    return max(max(MaxLeftSum, MaxRightSum), MaxRightBorderSum + MaxLeftBorderSum);
}
int main(){
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> num[i];
    cout << MaxSubSequenceSum(num, 0, n - 1) << endl;
    return 0;
} //go to hell
动态规划解法

上面的分治递归求解是不是感觉很高大上,把C++写出了Java的感觉

事实上费力不讨好,还不是被动态规划一棒子撂倒

下面,我们考虑用标准的动态规划来求解。

现进行如下思考:

1.令状态dp[i]表示以A[i]作为末尾的连续序列的最大和,所求的东西就是dp数组中的最大值。

2.做如下考虑:因为dp[i]要求是必须以A[i]结尾的连续序列,只有两种情况:

这个最大和的连续序列只有一个元素,即以A[i]开始,以A[i]结尾;这个最大和的连续序列有多个元素

于是得到状态转移方程:

这个式子只和i与i之前的元素有关,边界条件是dp[1] = A[1],dp数组的最大值就是答案。

算法的时间复杂度为

代码如下:

#include 
#include 
using namespace std;
const int N = 10010;
int A[N], dp[N];
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++) {
        cin >> A[i];
    }
    dp[1] = A[1];
    for(int i = 2; i <= n; i ++) {
        dp[i] = max(A[i], dp[i - 1] + A[i]);
    }
    int ans = 0;
    for(int i = 1; i <= n; i ++) {
        ans = max(dp[i], ans);
    }
    cout << ans << 'n';

    return 0;
}
联机算法

个人认为最简单也最好想,本质上是动态规划的思想。

该算法对所有数据遍历一遍,一旦A[ i ]被读入并被处理,它就不再需要被记忆。由于该问题不需要最大序列和的坐标输出,所以我们不需要记忆序列位置,仅仅记忆当前最大序列和的值即可。

算法的时间复杂度为

代码如下:

#include 
using namespace std;
const int N = 1010;
int num[N];

int MaxSubSequenceSum(const int A[ ], int N){
	int ThisSum, MaxSum, j;
 
	ThisSum = MaxSum = 0;
	for (j= 0; j < N; j++) {
		ThisSum += A[j];
		if (ThisSum > MaxSum)
		    MaxSum = ThisSum;
        if(ThisSum < 0)
			ThisSum = 0;
	}
	return MaxSum;
}
int main(){
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> num[i];
    cout << MaxSubSequenceSum(num, n - 1) << endl;
    return 0;
}
写在最后

最后...如果你有兴趣的话,欢迎来挑战这个问题的加强版本!

动态规划之最大两段连续子序列和_AryCra_07的博客-CSDN博客

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5713890.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存