爆刷PAT(甲级)——之【1007】 Maximum Subsequence Sum (25)——最大子序列和,思维

本文解析了一种求解整数序列中最大子序列和及其首末项元素的算法,通过一次遍历实现O(N)的时间复杂度。讨论了算法背后的思维逻辑,包括如何处理负数和全部为负数的情况,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意: 给出一个整数序列,长度为N。然后输出这个序列的 “最大子序列和” 和以及子序列的首项、末项。

难点:

1、题目分析

N最大10000,肯定要用O(N)的算法。其实想一下,这就是一个思维题,从左边扫区间到右边,不断保存当前的累加值,用这个累加值去维护当前的最大子序列和,以及记录最大的子序列的区间范围即可

如果当前的值(和)大于已有的最大序列和,就覆盖保存;

如果当前的值(和)已经小于0了,说明当前节点是负数,而且前面一段节点都是负数。那最大序列和是不可能从这出发的。所以直接令当前的值(和)为0,从0开始重新累加即可。

扫完一遍,维护好的最大值输出即可。

2、思维要点

思维要点就一个,要理解这种扫法是可以出解的:

假设一个序列是   -1 -1 3 -1 -1 3 -1 -1

那么我们可以发现最大区间是第一个3到第二个3,这一段的区间和是 4。

这说明了什么——说明了,找区间的时候,并不能仅仅考虑  累加每一段正数区间。因为有可能正数区间之间是可以选择到同一个区间后,累加的值更大,才是答案!

那如何判断两个正数区间,夹杂着一些负数的时候,什么时候把它们合并成一个区间呢?

再看这个例子:

假设一个序列是   -1 -1 3 -1 -1  -1 3 -1 -1

或者是  -1 -1 3 -1  -1   -1  -1 3 -1 -1

我们就会发现,这两个序列,第一个序列要不要合并最大值都是3;第二个序列合并了两个3的区间之后,最大值成了2反而少了还不如不合并。

也就是说,得出结论——从左边向右边,对这个数组求和,如果当前的和sum<0,那么就肯定!肯定!不需要再把前面这段的区间和还拿到后面累加上去,然后讨论最大值了!而是,可以直接舍弃掉!也就是令sum为0!

这就是我前面概括的本题的思维点。即可A题。

3、坑点

要注意审题——题目说,如果整个区间里全是负数,那么输出的和为 0!!!!  以及把区间第一个数和最后一个数输出!

还有一个小细节,如果是  -1 0 1的话,那还是要输出 0 的,标准输出结果是  “0 0 0”;如果是 0 0 0序列的话,输出结果也应该是“0 0 0”,也就说算法还没开始的时候,最小值应该初始化为负数,而不是0.。。。

 

还是一道水题,大一已经做过了。今天话痨一下。

Code:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define inf 10009
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;x<z;x++)

int n;
int num[inf];
int max_sum,sum;//最大和,以及当前和
int l,r;//临时表示的区间
int max_l,max_r;//最大和 的区间
int tag=0;//全0标志,1表示全0

void Input()
{
    cin>>n;
    int i;
    loop(i,0,n)
    {
        cin>>num[i];
        if(num[i]>=0)tag=1;
    }
}

void Solve()
{
    if(!tag)return;
    int i;
    max_sum=-INF;
    sum=0;//Init

    loop(i,0,n)
    {
        sum+=num[i];
        if(sum>max_sum)
        {
            max_sum=sum;
            max_l=l;
            r=i;
        }
        if(sum<=0)
        {
            sum=0;
            l=i+1;
        }
    }
}

void Output()
{
    if(!tag)//全0
        cout<<"0 "<<num[0]<<' '<<num[n-1]<<endl;
    else
    {
        max_r=r;
        cout<<max_sum<<' '<<num[max_l]<<' '<<num[max_r]<<endl;
    }
}

int main()
{
    Input();
    Solve();
    Output();
    return 0;
}

 

### 最大子序列问题的解决方法 最大子序列问题是经典的算法问题之一,目标是从给定数组中找到一个连续子序列,使得该子序列中的元素之达到最大值。以下是基于动态规划的思想实现的一个高效解决方案。 #### 动态规划法 通过维护两个变量 `current_sum` `max_sum` 来记录当前子序列最大以及全局范围内的最大。遍历整个数组一次即可完成计算: ```python def max_subsequence_sum(nums): current_sum = 0 max_sum = float('-inf') # 初始化为负无穷大 for num in nums: current_sum = max(num, current_sum + num) # 更新当前子序列 max_sum = max(max_sum, current_sum) # 更新全局最大 return max_sum ``` 上述代码的时间复杂度为 \(O(n)\),其中 \(n\) 是输入列表的长度[^1]。 #### 示例运行 假设我们有如下输入数据: ```python nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] result = max_subsequence_sum(nums) print(result) # 输出应为6 (子序列为 [4,-1,2,1]) ``` 此方法的核心在于每次迭代都决定是否将当前数加入到现有子序列或者重新开始一个新的子序列。 #### 非连续子序列的情况 如果允许选取非连续的子序列,则可以采用贪心策略来解决问题。对于这个问题的具体实现方式已经在 JavaScript 的例子中有体现。然而,在 Python 中可以通过简单的排序加累加操作快速得到结果: ```python def non_contiguous_max_subsequence_sum(nums): positive_nums = sorted([num for num in nums if num > 0], reverse=True) total = sum(positive_nums) return total if total != 0 else max(nums) # 测试用例 nums = [7, 2, -8, 4, 10, -2] result = non_contiguous_max_subsequence_sum(nums) print(result) # 应输出23 ``` 这里需要注意的是当所有数值均为负数时需单独处理以确保返回最大的单个元素作为结果。 ### 结论 无论是针对连续还是非连续情况下的最大子序列问题都可以借助不同的优化手段有效解决。前者依赖于线性的扫描过程而后者则可能涉及更复杂的逻辑判断或额外的数据结构支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值